Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/fixtures/twoNodeExpansionFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import RBush from "rbush"
import type { RectDiffExpansionSolverInput } from "../solvers/RectDiffExpansionSolver/RectDiffExpansionSolver"
import type { SimpleRouteJson } from "../types/srj-types"
import type { XYRect } from "../rectdiff-types"
import type { RTreeRect } from "lib/types/capacity-mesh-types"
import type { RTreeRect } from "../types/capacity-mesh-types"
import { buildZIndexMap } from "../solvers/RectDiffSeedingSolver/layers"

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/solvers/GapFillSolver/ExpandEdgesToEmptySpaceSolver.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { BaseSolver } from "@tscircuit/solver-utils"
import type { CapacityMeshNode } from "lib/types/capacity-mesh-types"
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
import type { SegmentWithAdjacentEmptySpace } from "./FindSegmentsWithAdjacentEmptySpaceSolver"
import type { GraphicsObject } from "graphics-debug"
import RBush from "rbush"
import { EDGE_MAP, EDGES } from "./edge-constants"
import { getBoundsFromCorners } from "./getBoundsFromCorners"
import type { Bounds } from "@tscircuit/math-utils"
import { midpoint, segmentToBoxMinDistance } from "@tscircuit/math-utils"
import type { XYRect } from "lib/rectdiff-types"
import type { XYRect } from "../../rectdiff-types"

const EPS = 1e-4

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BaseSolver } from "@tscircuit/solver-utils"
import Flatbush from "flatbush"
import type { GraphicsObject, NinePointAnchor } from "graphics-debug"
import type { CapacityMeshNode } from "lib/types/capacity-mesh-types"
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
import { projectToUncoveredSegments } from "./projectToUncoveredSegments"
import { EDGES } from "./edge-constants"
import { visuallyOffsetLine } from "./visuallyOffsetLine"
Expand Down
4 changes: 2 additions & 2 deletions lib/solvers/GapFillSolver/GapFillSolverPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {
definePipelineStep,
type PipelineStep,
} from "@tscircuit/solver-utils"
import type { CapacityMeshNode } from "lib/types/capacity-mesh-types"
import type { CapacityMeshNode } from "../../types/capacity-mesh-types"
import type { GraphicsObject } from "graphics-debug"
import { FindSegmentsWithAdjacentEmptySpaceSolver } from "./FindSegmentsWithAdjacentEmptySpaceSolver"
import { ExpandEdgesToEmptySpaceSolver } from "./ExpandEdgesToEmptySpaceSolver"
import type { XYRect } from "lib/rectdiff-types"
import type { XYRect } from "../../rectdiff-types"

type GapFillSolverInput = {
meshNodes: CapacityMeshNode[]
Expand Down
87 changes: 85 additions & 2 deletions lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { BaseSolver } from "@tscircuit/solver-utils"
import type { GraphicsObject } from "graphics-debug"
import type { CapacityMeshNode, RTreeRect } from "lib/types/capacity-mesh-types"
import type {
CapacityMeshNode,
RTreeRect,
} from "../../types/capacity-mesh-types"
import { expandRectFromSeed } from "../../utils/expandRectFromSeed"
import { finalizeRects } from "../../utils/finalizeRects"
import { resizeSoftOverlaps } from "../../utils/resizeSoftOverlaps"
import { rectsToMeshNodes } from "./rectsToMeshNodes"
import type { XYRect, Candidate3D, Placed3D } from "../../rectdiff-types"
import type { Obstacle } from "lib/types/srj-types"
import type { Obstacle } from "../../types/srj-types"
import RBush from "rbush"
import { rectToTree } from "../../utils/rectToTree"
import { sameTreeRect } from "../../utils/sameTreeRect"
import { overlaps } from "../../utils/rectdiff-geometry"

export type RectDiffExpansionSolverInput = {
layerNames: string[]
Expand Down Expand Up @@ -104,6 +108,7 @@ export class RectDiffExpansionSolver extends BaseSolver {
zLayers: p.zLayers,
})

const rectToUse = expanded ?? oldRect
if (expanded) {
// Update placement + per-layer index (replace old rect object)
this.input.placed[idx] = { rect: expanded, zLayers: p.zLayers }
Expand All @@ -127,9 +132,87 @@ export class RectDiffExpansionSolver extends BaseSolver {
)
}

this.expandPlacementLayers(idx, rectToUse)

this.input.expansionIndex += 1
}

private expandPlacementLayers(idx: number, rect: XYRect) {
const placement = this.input.placed[idx]
if (!placement) return

const currentLayers = placement.zLayers.slice().sort((a, b) => a - b)
if (currentLayers.length === 0) return

const query = {
minX: rect.x,
minY: rect.y,
maxX: rect.x + rect.width,
maxY: rect.y + rect.height,
}

const isLayerFree = (layer: number) => {
const placedIndex = this.placedIndexByLayer[layer]
if (placedIndex) {
const hits = placedIndex.search(query)
for (const hit of hits) {
if (!overlaps(rect, hit)) continue
if (hit.zLayers.length >= this.input.layerCount) return false
}
}

return true
}

const minLayer = currentLayers[0]!
const maxLayer = currentLayers[currentLayers.length - 1]!
let nextMin = minLayer
let nextMax = maxLayer

while (nextMin - 1 >= 0 && isLayerFree(nextMin - 1)) {
nextMin -= 1
}

while (nextMax + 1 < this.input.layerCount && isLayerFree(nextMax + 1)) {
nextMax += 1
}

if (nextMin === minLayer && nextMax === maxLayer) return

const nextLayers: number[] = []
for (let z = nextMin; z <= nextMax; z += 1) {
nextLayers.push(z)
}

for (const z of currentLayers) {
const tree = this.placedIndexByLayer[z]
if (tree) {
tree.remove(rectToTree(rect, { zLayers: currentLayers }), sameTreeRect)
tree.insert(rectToTree(rect, { zLayers: nextLayers }))
}
}

for (const z of nextLayers) {
if (currentLayers.includes(z)) continue
const tree = this.placedIndexByLayer[z]
if (tree) {
tree.insert(rectToTree(rect, { zLayers: nextLayers }))
}
}

this.input.placed[idx] = { rect, zLayers: nextLayers }

resizeSoftOverlaps(
{
layerCount: this.input.layerCount,
placed: this.input.placed,
options: this.input.options,
placedIndexByLayer: this.placedIndexByLayer,
},
idx,
)
}

private finalizeIfNeeded() {
if (this.solved) return

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import type {
Obstacle,
SimpleRouteConnection,
SimpleRouteJson,
} from "lib/types/srj-types"
import type { GridFill3DOptions, XYRect } from "lib/rectdiff-types"
import type { CapacityMeshNode, RTreeRect } from "lib/types/capacity-mesh-types"
import { RectDiffSeedingSolver } from "lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver"
import { RectDiffExpansionSolver } from "lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver"
} from "../../types/srj-types"
import type { GridFill3DOptions, XYRect } from "../../rectdiff-types"
import type {
CapacityMeshNode,
RTreeRect,
} from "../../types/capacity-mesh-types"
import { RectDiffSeedingSolver } from "../RectDiffSeedingSolver/RectDiffSeedingSolver"
import { RectDiffExpansionSolver } from "../RectDiffExpansionSolver/RectDiffExpansionSolver"
import type { GraphicsObject } from "graphics-debug"
import RBush from "rbush"
import { buildObstacleIndexesByLayer } from "./buildObstacleIndexes"
Expand Down
12 changes: 6 additions & 6 deletions lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { SimpleRouteJson } from "lib/types/srj-types"
import type { SimpleRouteJson } from "../../types/srj-types"
import RBush from "rbush"
import { computeInverseRects } from "lib/solvers/RectDiffSeedingSolver/computeInverseRects"
import { computeInverseRects } from "../RectDiffSeedingSolver/computeInverseRects"
import {
buildZIndexMap,
obstacleToXYRect,
obstacleZs,
} from "lib/solvers/RectDiffSeedingSolver/layers"
import type { XYRect } from "lib/rectdiff-types"
import type { RTreeRect } from "lib/types/capacity-mesh-types"
import { padRect } from "lib/utils/padRect"
} from "../RectDiffSeedingSolver/layers"
import type { XYRect } from "../../rectdiff-types"
import type { RTreeRect } from "../../types/capacity-mesh-types"
import { padRect } from "../../utils/padRect"

export const buildObstacleIndexesByLayer = (params: {
srj: SimpleRouteJson
Expand Down
33 changes: 21 additions & 12 deletions lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import { computeCandidates3D } from "./computeCandidates3D"
import { computeEdgeCandidates3D } from "./computeEdgeCandidates3D"
import { longestFreeSpanAroundZ } from "./longestFreeSpanAroundZ"
import { allLayerNode } from "../../utils/buildHardPlacedByLayer"
import { isFullyOccupiedAtPoint } from "lib/utils/isFullyOccupiedAtPoint"
import { isFullyOccupiedAtPoint } from "../../utils/isFullyOccupiedAtPoint"
import { resizeSoftOverlaps } from "../../utils/resizeSoftOverlaps"
import { getColorForZLayer } from "lib/utils/getColorForZLayer"
import { getColorForZLayer } from "../../utils/getColorForZLayer"
import RBush from "rbush"
import type { RTreeRect } from "lib/types/capacity-mesh-types"
import { rectToTree } from "lib/utils/rectToTree"
import type { RTreeRect } from "../../types/capacity-mesh-types"
import { rectToTree } from "../../utils/rectToTree"

export type RectDiffSeedingSolverInput = {
simpleRouteJson: SimpleRouteJson
Expand Down Expand Up @@ -102,8 +102,8 @@ export class RectDiffSeedingSolver extends BaseSolver {
maxAspectRatio: 3,
minSingle: { width: 2 * trace, height: 2 * trace },
minMulti: {
width: 4 * trace,
height: 4 * trace,
width: 2 * trace,
height: 2 * trace,
minLayers: Math.min(2, Math.max(1, srj.layerCount || 1)),
},
preferMultiLayer: true,
Expand Down Expand Up @@ -243,19 +243,28 @@ export class RectDiffSeedingSolver extends BaseSolver {
}> = []

if (span.length >= minMulti.minLayers) {
attempts.push({
kind: "multi",
layers: span,
minReq: { width: minMulti.width, height: minMulti.height },
})
const spanIndex = span.indexOf(cand.z)
let lo = 0
let hi = span.length - 1

while (hi - lo + 1 >= minMulti.minLayers) {
attempts.push({
kind: "multi",
layers: span.slice(lo, hi + 1),
minReq: { width: minMulti.width, height: minMulti.height },
})
if (hi - lo + 1 === minMulti.minLayers) break
if (spanIndex - lo > hi - spanIndex) lo += 1
else hi -= 1
}
}
attempts.push({
kind: "single",
layers: [cand.z],
minReq: { width: minSingle.width, height: minSingle.height },
})

const ordered = preferMultiLayer ? attempts : attempts.reverse()
const ordered = preferMultiLayer ? attempts : attempts.slice().reverse()

for (const attempt of ordered) {
const rect = expandRectFromSeed({
Expand Down
2 changes: 1 addition & 1 deletion lib/solvers/RectDiffSeedingSolver/computeCandidates3D.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EPS, distancePointToRectEdges } from "../../utils/rectdiff-geometry"
import { isFullyOccupiedAtPoint } from "../../utils/isFullyOccupiedAtPoint"
import { longestFreeSpanAroundZ } from "./longestFreeSpanAroundZ"
import type RBush from "rbush"
import type { RTreeRect } from "lib/types/capacity-mesh-types"
import type { RTreeRect } from "../../types/capacity-mesh-types"
const quantize = (value: number, precision = 1e-6) =>
Math.round(value / precision) * precision

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EPS, distancePointToRectEdges } from "../../utils/rectdiff-geometry"
import { isFullyOccupiedAtPoint } from "../../utils/isFullyOccupiedAtPoint"
import { longestFreeSpanAroundZ } from "./longestFreeSpanAroundZ"
import type RBush from "rbush"
import type { RTreeRect } from "lib/types/capacity-mesh-types"
import type { RTreeRect } from "../../types/capacity-mesh-types"
const quantize = (value: number, precision = 1e-6) =>
Math.round(value / precision) * precision

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { RTreeRect } from "lib/types/capacity-mesh-types"
import type { RTreeRect } from "../../types/capacity-mesh-types"
import type { XYRect } from "../../rectdiff-types"
import { clamp, containsPoint } from "../../utils/rectdiff-geometry"
import type RBush from "rbush"
Expand Down
2 changes: 1 addition & 1 deletion lib/types/capacity-mesh-types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { XYRect } from "lib/rectdiff-types"
import type { XYRect } from "../rectdiff-types"

export type CapacityMeshNodeId = string

Expand Down
2 changes: 1 addition & 1 deletion lib/utils/expandRectFromSeed.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type RBush from "rbush"
import type { XYRect } from "../rectdiff-types"
import { EPS, gt, gte, lt, lte, overlaps } from "./rectdiff-geometry"
import type { RTreeRect } from "lib/types/capacity-mesh-types"
import type { RTreeRect } from "../types/capacity-mesh-types"
import { isSelfRect } from "./isSelfRect"
import {
searchStripDown,
Expand Down
61 changes: 52 additions & 9 deletions lib/utils/finalizeRects.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Obstacle } from "lib/types/srj-types"
import type { Obstacle } from "../types/srj-types"
import type { Placed3D, Rect3d, XYRect } from "../rectdiff-types"
import { subtractRect2D } from "./rectdiff-geometry"
import {
obstacleToXYRect,
obstacleZs,
Expand All @@ -12,14 +13,8 @@ export function finalizeRects(params: {
zIndexByName: Map<string, number>
obstacleClearance?: number
}): Rect3d[] {
// Convert all placed (free space) nodes to output format
const out: Rect3d[] = params.placed.map((p) => ({
minX: p.rect.x,
minY: p.rect.y,
maxX: p.rect.x + p.rect.width,
maxY: p.rect.y + p.rect.height,
zLayers: [...p.zLayers].sort((a, b) => a - b),
}))
const out: Rect3d[] = []
const obstacleRectsByLayer = new Map<number, XYRect[]>()

const layersByKey = new Map<string, { rect: XYRect; layers: Set<number> }>()

Expand All @@ -38,6 +33,11 @@ export function finalizeRects(params: {
obstacle.zLayers?.length && obstacle.zLayers.length > 0
? obstacle.zLayers
: obstacleZs(obstacle, params.zIndexByName)
for (const layer of zLayers) {
const list = obstacleRectsByLayer.get(layer)
if (list) list.push(rect)
else obstacleRectsByLayer.set(layer, [rect])
}
const key = `${rect.x}:${rect.y}:${rect.width}:${rect.height}`
let entry = layersByKey.get(key)
if (!entry) {
Expand All @@ -58,5 +58,48 @@ export function finalizeRects(params: {
})
}

const freeRectsByKey = new Map<
string,
{ rect: XYRect; layers: Set<number> }
>()

for (const placed of params.placed) {
for (const layer of placed.zLayers) {
let parts: XYRect[] = [placed.rect]
const blockers = obstacleRectsByLayer.get(layer) ?? []
for (const blocker of blockers) {
const nextParts: XYRect[] = []
for (const part of parts) {
const carved = subtractRect2D(part, blocker)
nextParts.push(...carved)
}
parts = nextParts
if (parts.length === 0) break
}

for (const part of parts) {
const key = `${part.x}:${part.y}:${part.width}:${part.height}`
let entry = freeRectsByKey.get(key)
if (!entry) {
entry = { rect: part, layers: new Set() }
freeRectsByKey.set(key, entry)
}
entry.layers.add(layer)
}
}
}

for (const { rect, layers } of freeRectsByKey.values()) {
const layerList = Array.from(layers).sort((a, b) => a - b)
if (layerList.length === 0) continue
out.push({
minX: rect.x,
minY: rect.y,
maxX: rect.x + rect.width,
maxY: rect.y + rect.height,
zLayers: layerList,
})
}

return out
}
Loading
Loading