@@ -170,6 +170,59 @@ export class HyperGraphSolver<
170170 return candidates
171171 }
172172
173+ /**
174+ * OPTIONALLY OVERRIDE THIS
175+ *
176+ * Compute the full set of solved routes that must be ripped to accept
177+ * `newlySolvedRoute`. By default this returns all conflicting routes
178+ * (always-rip behavior)
179+ *
180+ * Override this to implement partial ripping, where only a subset of
181+ * conflicting routes are removed.
182+ */
183+ computeRoutesToRip ( newlySolvedRoute : SolvedRoute ) : Set < SolvedRoute > {
184+ const crossingRoutesToRip = this . computeCrossingRoutes ( newlySolvedRoute )
185+ const portReuseRoutesToRip = this . computePortOverlapRoutes ( newlySolvedRoute )
186+ return new Set < SolvedRoute > ( [
187+ ...crossingRoutesToRip ,
188+ ...portReuseRoutesToRip ,
189+ ] )
190+ }
191+
192+ /**
193+ * Returns solved routes that overlap ports with the newly solved route.
194+ * Use this in computeRoutesToRip overrides to include port reuse rips.
195+ */
196+ computePortOverlapRoutes ( newlySolvedRoute : SolvedRoute ) : Set < SolvedRoute > {
197+ const portReuseRoutesToRip : Set < SolvedRoute > = new Set ( )
198+ for ( const candidate of newlySolvedRoute . path ) {
199+ if (
200+ candidate . port . assignment &&
201+ candidate . port . assignment . connection . mutuallyConnectedNetworkId !==
202+ newlySolvedRoute . connection . mutuallyConnectedNetworkId
203+ ) {
204+ portReuseRoutesToRip . add ( candidate . port . assignment . solvedRoute )
205+ }
206+ }
207+ return portReuseRoutesToRip
208+ }
209+
210+ computeCrossingRoutes ( newlySolvedRoute : SolvedRoute ) : Set < SolvedRoute > {
211+ const crossingRoutesToRip : Set < SolvedRoute > = new Set ( )
212+ for ( const candidate of newlySolvedRoute . path ) {
213+ if ( ! candidate . lastPort || ! candidate . lastRegion ) continue
214+ const ripsRequired = this . getRipsRequiredForPortUsage (
215+ candidate . lastRegion as RegionType ,
216+ candidate . lastPort as RegionPortType ,
217+ candidate . port as RegionPortType ,
218+ )
219+ for ( const assignment of ripsRequired ) {
220+ crossingRoutesToRip . add ( assignment . solvedRoute )
221+ }
222+ }
223+ return crossingRoutesToRip
224+ }
225+
173226 getNextCandidates ( currentCandidate : CandidateType ) : CandidateType [ ] {
174227 const currentRegion = currentCandidate . nextRegion !
175228 const currentPort = currentCandidate . port
@@ -234,38 +287,16 @@ export class HyperGraphSolver<
234287 cursorCandidate = cursorCandidate . parent as CandidateType | undefined
235288 }
236289
237- // Rip any routes that are connected to the solved route (port reuse) and requeue
238- const routesToRip : Set < SolvedRoute > = new Set ( )
239290 if ( anyRipsRequired ) {
240291 solvedRoute . requiredRip = true
241- for ( const candidate of solvedRoute . path ) {
242- if (
243- candidate . port . assignment &&
244- candidate . port . assignment . connection . mutuallyConnectedNetworkId !==
245- this . currentConnection ! . mutuallyConnectedNetworkId
246- ) {
247- routesToRip . add ( candidate . port . assignment . solvedRoute )
248- }
249- }
250292 }
251293
252- // Check for rips required due to port usage (crossing assignments)
253- for ( const candidate of solvedRoute . path ) {
254- if ( ! candidate . lastPort || ! candidate . lastRegion ) continue
255- const ripsRequired = this . getRipsRequiredForPortUsage (
256- candidate . lastRegion as RegionType ,
257- candidate . lastPort as RegionPortType ,
258- candidate . port as RegionPortType ,
259- )
260- for ( const assignment of ripsRequired ) {
261- routesToRip . add ( assignment . solvedRoute )
262- }
263- }
294+ const allRoutesToRip = this . computeRoutesToRip ( solvedRoute )
264295
265- // Perform the ripping
266- if ( routesToRip . size > 0 ) {
296+ // Rip conflicting routes before committing assignments.
297+ if ( allRoutesToRip . size > 0 ) {
267298 solvedRoute . requiredRip = true
268- for ( const route of routesToRip ) {
299+ for ( const route of allRoutesToRip ) {
269300 this . ripSolvedRoute ( route )
270301 }
271302 }
0 commit comments