Skip to content

Commit

Permalink
wip schematic utility methods
Browse files Browse the repository at this point in the history
  • Loading branch information
seveibar committed Aug 25, 2024
1 parent 7c6d58d commit 5310ed4
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,5 @@ dist
.vscode
*.diff.png
.aider*

*.old.ts
8 changes: 8 additions & 0 deletions lib/utils/range.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// same signature as lodash.range
export const range = (start: number, end: number, step = 1) => {
const result: number[] = []
for (let i = start; i < end; i += step) {
result.push(i)
}
return result
}
260 changes: 260 additions & 0 deletions lib/utils/schematic/get-port-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
import { range } from "lib/utils/range"

export type VerticalPortSideConfiguration = {
pin_definition_direction?: "top-to-bottom" | "bottom-to-top"
pins: number[]
}
export type HorizontalPortSideConfiguration = {
pin_definition_direction?: "left-to-right" | "right-to-left"
pins: number[]
}

export type ExplicitPinMappingArrangement = {
left_side?: VerticalPortSideConfiguration
right_side?: VerticalPortSideConfiguration
top_side?: HorizontalPortSideConfiguration
bottom_side?: HorizontalPortSideConfiguration
}
export type SideSizes = {
left_size?: number
right_size?: number
top_size?: number
bottom_size?: number
}

export type PortArrangement = SideSizes | ExplicitPinMappingArrangement

export type ExtendedPortArrangement = PortArrangement & {
pin_spacing?: number
}

export const hasExplicitPinMapping = (
pa: PortArrangement,
): pa is ExplicitPinMappingArrangement => {
for (const side of ["left_side", "right_side", "top_side", "bottom_side"]) {
if (side in pa && typeof pa[side] === "number") {
throw new Error(
`A number was specified for "${side}", try using "${side.replace(
"side",
"size",
)}"`,
)
}
}
return (
"left_side" in pa ||
"right_side" in pa ||
"top_side" in pa ||
"bottom_side" in pa
)
}

export const getNormalToExplicitPinMapping = (
pa: ExplicitPinMappingArrangement,
): number[] => {
const normal_to_explicit: number[] = [0]
const { left_side, right_side, top_side, bottom_side } = pa
for (const [side, normalDirection] of [
[left_side, "top-to-bottom"],
[bottom_side, "left-to-right"],
[right_side, "bottom-to-top"],
[top_side, "right-to-left"],
] as const) {
if (side) {
const definedOrderNormal =
side.pin_definition_direction === undefined ||
side.pin_definition_direction === normalDirection

const definedPins = [...side.pins]
if (!definedOrderNormal) {
definedPins.reverse()
}
for (let i = 0; i < definedPins.length; i++) {
normal_to_explicit.push(definedPins[i])
}
}
}
return normal_to_explicit
}

export const getExplicitToNormalPinMapping = (
pa: ExplicitPinMappingArrangement,
): number[] => {
const normal_to_explicit: number[] = getNormalToExplicitPinMapping(pa)
const explicit_to_normal: number[] = []
for (let i = 0; i < normal_to_explicit.length; i++) {
explicit_to_normal[normal_to_explicit[i]] = i
}
return explicit_to_normal
}

export const getSizeOfSidesFromPortArrangement = (
pa: PortArrangement,
): {
left_size: number
right_size: number
top_size: number
bottom_size: number
} => {
if (hasExplicitPinMapping(pa)) {
return {
left_size: pa.left_side?.pins.length ?? 0,
right_size: pa.right_side?.pins.length ?? 0,
top_size: pa.top_side?.pins.length ?? 0,
bottom_size: pa.bottom_side?.pins.length ?? 0,
}
}
const {
left_size = 0,
right_size = 0,
top_size = 0,
bottom_size = 0,
} = pa as any
return { left_size, right_size, top_size, bottom_size }
}

/**
* Minimum distance between two sides, e.g. if there is only a left_size and
* right_size provided (only pins on left and right) then the minimum distance
* is the x distance between the left and right ports.
*/
const MIN_SIDE_DIST = 1.5

/**
* Distance between ports on the same side
*/
export const DEFAULT_PIN_SPACING = 0.5

/**
* These are all the defined port indices, for regular bugs all ports are
* defined but some bugs have a lot of unused pins, so there will be skipped
* ports.
*/
export const getPortIndices = (pa: PortArrangement): number[] => {
if (hasExplicitPinMapping(pa)) {
const port_indices: number[] = []
for (const p of [
...(pa.left_side?.pins ?? []),
...(pa.bottom_side?.pins ?? []),
...(pa.right_side?.pins ?? []),
...(pa.top_side?.pins ?? []),
]) {
port_indices.push(p)
}
return port_indices
}
const { left_size, right_size, top_size, bottom_size } =
getSizeOfSidesFromPortArrangement(pa)

return _.range(1, left_size + right_size + top_size + bottom_size + 1)
}

export const getPortArrangementSize = (
port_arrangement: ExtendedPortArrangement,
): { width: number; height: number; total_ports: number } => {
const pinSpacing = port_arrangement.pin_spacing ?? DEFAULT_PIN_SPACING
const {
top_size = 0,
right_size = 0,
bottom_size = 0,
left_size = 0,
} = getSizeOfSidesFromPortArrangement(port_arrangement)

const total_ports = top_size + right_size + bottom_size + left_size

const width = Math.max(
// MIN_SIDE_DIST is multiplied by the ratio of pin spacing to create more
// square-like bugs
MIN_SIDE_DIST * (pinSpacing / DEFAULT_PIN_SPACING),
(top_size + 1) * pinSpacing,
(bottom_size + 1) * pinSpacing,
)
const height = Math.max(
MIN_SIDE_DIST,
(left_size + 1) * pinSpacing,
(right_size + 1) * pinSpacing,
)
return { width, height, total_ports }
}

/**
* Get the position of a port given a port arrangement. The position corresponds
* to the index of the port if you travel counter-clockwise starting from the
* top-left and traveling down the left side. The index begins with 1.
*/
export const getPortPosition = (
port_arrangement: ExtendedPortArrangement,
position: number,
): {
x: number
y: number
side: "top" | "bottom" | "left" | "right"
} => {
const pin_spacing = port_arrangement.pin_spacing ?? DEFAULT_PIN_SPACING
const {
top_size = 0,
right_size = 0,
bottom_size = 0,
left_size = 0,
} = getSizeOfSidesFromPortArrangement(port_arrangement)
const total = top_size + right_size + bottom_size + left_size
const port_indices = getPortIndices(port_arrangement)
if (!port_indices.includes(position)) {
throw new Error(
`Invalid position ${position} on port arrangement with available ports: ${port_indices.join(
" ",
)}`,
)
}

if (hasExplicitPinMapping(port_arrangement)) {
// Map position to equivalent position in a normal port mapping
const og_p = position
position = getExplicitToNormalPinMapping(port_arrangement)[position]
// console.log("original position:", og_p, "mapped to:", position)
}

let side: "top" | "bottom" | "left" | "right"
let index: number
if (position <= left_size) {
side = "left"
index = position - 1
} else if (position <= bottom_size + left_size) {
side = "bottom"
index = position - left_size - 1
} else if (position <= bottom_size + left_size + right_size) {
side = "right"
index = position - left_size - bottom_size - 1
} else {
side = "top"
index = position - left_size - bottom_size - right_size - 1
}

const { width, height } = getPortArrangementSize(port_arrangement)

let x: number
let y: number
if (side === "top") {
const i_dist_center = index - (top_size - 1) / 2
x = i_dist_center * pin_spacing
y = height / 2
} else if (side === "bottom") {
const i_dist_center = index - (bottom_size - 1) / 2
x = i_dist_center * pin_spacing
y = -height / 2
} else if (side === "left") {
const i_dist_center = -index + (left_size - 1) / 2
y = i_dist_center * pin_spacing
x = -width / 2
} else if (side === "right") {
const i_dist_center = index - (right_size - 1) / 2
y = i_dist_center * pin_spacing
x = width / 2
} else {
throw new Error(`Invalid side "${side}", can't set x/y values`)
}

return { x, y, side }
}

export default getPortPosition

0 comments on commit 5310ed4

Please sign in to comment.