Skip to content

Commit

Permalink
Merge pull request #4 from tscircuit/add-transforms
Browse files Browse the repository at this point in the history
Add Transforms
  • Loading branch information
seveibar authored Jul 11, 2024
2 parents 603da28 + edd3519 commit 6689228
Show file tree
Hide file tree
Showing 7 changed files with 429 additions and 153 deletions.
41 changes: 41 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
"organizeImports": {
"enabled": true
},
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"javascript": {
"formatter": {
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "all",
"semicolons": "asNeeded",
"arrowParentheses": "always",
"bracketSpacing": true,
"bracketSameLine": false
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "off"
},
"style": {
"noNonNullAssertion": "off",
"useFilenamingConvention": {
"level": "error",
"options": {
"strictCase": true,
"requireAscii": true,
"filenameCases": ["kebab-case", "export"]
}
}
}
}
}
}
139 changes: 4 additions & 135 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,136 +1,5 @@
import type {
AnySoupElement,
AnySoupElementInput,
SourceComponentBase,
SourcePort,
} from "@tscircuit/soup"
export * from "./lib/su"
export * from "./lib/transform-soup-elements"
export * from "./lib/direction-to-vec"

type SoupOps<
K extends AnySoupElement["type"],
T extends AnySoupElement | AnySoupElementInput
> = {
get: (id: string) => Extract<T, { type: K }> | null
select: (selector: string) => Extract<T, { type: K }> | null
getWhere: (where: any) => Extract<T, { type: K }> | null
getUsing: (using: {
[key: `${string}_id`]: string
}) => Extract<T, { type: K }> | null
list: (where?: any) => Extract<T, { type: K }>[]
}

export type SoupUtilObject = {
[K in AnySoupElement["type"]]: SoupOps<K, AnySoupElement>
}
export type SoupInputUtilObject = {
[K in AnySoupElementInput["type"]]: SoupOps<K, AnySoupElementInput>
}

export type GetSoupUtilObject = ((soup: AnySoupElement[]) => SoupUtilObject) & {
unparsed: (soup: AnySoupElementInput[]) => SoupInputUtilObject
}

export const su: GetSoupUtilObject = ((soup: AnySoupElement[]) => {
const su = new Proxy(
{},
{
get: (proxy_target: any, component_type: string) => {
return {
get: (id: string) =>
soup.find(
(e: any) =>
e.type === component_type && e[`${component_type}_id`] === id
),
getUsing: (using: any) => {
const keys = Object.keys(using)
if (keys.length !== 1) {
throw new Error(
"getUsing requires exactly one key, e.g. { pcb_component_id }"
)
}
const join_key = keys[0] as string
const join_type = join_key.replace("_id", "")
const joiner: any = soup.find(
(e: any) =>
e.type === join_type && e[join_key] === using[join_key]
)
if (!joiner) return null
return soup.find(
(e: any) =>
e.type === component_type &&
e[`${component_type}_id`] === joiner[`${component_type}_id`]
)
},
getWhere: (where: any) => {
const keys = Object.keys(where)
return soup.find(
(e: any) =>
e.type === component_type &&
keys.every((key) => e[key] === where[key])
)
},
list: (where?: any) => {
const keys = !where ? [] : Object.keys(where)
return soup.filter(
(e: any) =>
e.type === component_type &&
keys.every((key) => e[key] === where[key])
)
},
select: (selector: string) => {
// TODO when applySelector is isolated we can use it, until then we
// do a poor man's selector implementation for two common cases
if (component_type === "source_component") {
return soup.find(
(e) =>
e.type === "source_component" &&
e.name === selector.replace(/\./g, "")
)
} else if (
component_type === "pcb_port" ||
component_type === "source_port" ||
component_type === "schematic_port"
) {
const [component_name, port_selector] = selector
.replace(/\./g, "")
.split(/[\s\>]+/)
const source_component = soup.find(
(e) =>
e.type === "source_component" && e.name === component_name
) as SourceComponentBase
if (!source_component) return null
const source_port = soup.find(
(e) =>
e.type === "source_port" &&
e.source_component_id ===
source_component.source_component_id &&
(e.name === port_selector ||
(e.port_hints ?? []).includes(port_selector!))
) as SourcePort
if (!source_port) return null
if (component_type === "source_port") return source_port

if (component_type === "pcb_port") {
return soup.find(
(e) =>
e.type === "pcb_port" &&
e.source_port_id === source_port.source_port_id
)
} else if (component_type === "schematic_port") {
return soup.find(
(e) =>
e.type === "schematic_port" &&
e.source_port_id === source_port.source_port_id
)
}
}
},
}
},
}
)

return su
}) as any
su.unparsed = su as any

export default su
export { default as su } from "./lib/su"
72 changes: 72 additions & 0 deletions lib/direction-to-vec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export const directionToVec = (direction: "up" | "down" | "left" | "right") => {
if (direction === "up") return { x: 0, y: 1 }
else if (direction === "down") return { x: 0, y: -1 }
else if (direction === "left") return { x: -1, y: 0 }
else if (direction === "right") return { x: 1, y: 0 }
else throw new Error("Invalid direction")
}

export const vecToDirection = ({ x, y }: { x: number; y: number }) => {
if (x > y) y = 0
if (y > x) x = 0
if (x > 0 && y === 0) return "right"
else if (x < 0 && y === 0) return "left"
else if (x === 0 && y > 0) return "up"
else if (x === 0 && y < 0) return "down"
else throw new Error(`Invalid vector for direction conversion (${x}, ${y})`)
}

export const rotateClockwise = (
direction: "up" | "down" | "left" | "right"
) => {
if (direction === "up") return "right"
else if (direction === "right") return "down"
else if (direction === "down") return "left"
else if (direction === "left") return "up"
throw new Error(`Invalid direction: ${direction}`)
}

export const rotateCounterClockwise = (
direction: "up" | "down" | "left" | "right"
) => {
if (direction === "up") return "left"
else if (direction === "left") return "down"
else if (direction === "down") return "right"
else if (direction === "right") return "up"
throw new Error(`Invalid direction: ${direction}`)
}

export const rotateDirection = (
direction: "up" | "down" | "left" | "right",
num90DegreeClockwiseTurns: number
) => {
while (num90DegreeClockwiseTurns > 0) {
direction = rotateClockwise(direction)
num90DegreeClockwiseTurns--
}
while (num90DegreeClockwiseTurns < 0) {
direction = rotateCounterClockwise(direction)
num90DegreeClockwiseTurns++
}
return direction
}

export const oppositeDirection = (
direction: "up" | "down" | "left" | "right"
) => {
if (direction === "up") return "down"
else if (direction === "down") return "up"
else if (direction === "left") return "right"
else if (direction === "right") return "left"
throw new Error(`Invalid direction: ${direction}`)
}

export const oppositeSide = (
sideOrDir: "up" | "down" | "top" | "bottom" | "left" | "right"
) => {
if (sideOrDir === "top" || sideOrDir === "up") return "bottom"
else if (sideOrDir === "bottom" || sideOrDir === "down") return "top"
else if (sideOrDir === "left") return "right"
else if (sideOrDir === "right") return "left"
throw new Error(`Invalid sideOrDir: ${sideOrDir}`)
}
136 changes: 136 additions & 0 deletions lib/su.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import type {
AnySoupElement,
AnySoupElementInput,
SourceComponentBase,
SourcePort,
} from "@tscircuit/soup"

type SoupOps<
K extends AnySoupElement["type"],
T extends AnySoupElement | AnySoupElementInput
> = {
get: (id: string) => Extract<T, { type: K }> | null
select: (selector: string) => Extract<T, { type: K }> | null
getWhere: (where: any) => Extract<T, { type: K }> | null
getUsing: (using: {
[key: `${string}_id`]: string
}) => Extract<T, { type: K }> | null
list: (where?: any) => Extract<T, { type: K }>[]
}

export type SoupUtilObject = {
[K in AnySoupElement["type"]]: SoupOps<K, AnySoupElement>
}
export type SoupInputUtilObject = {
[K in AnySoupElementInput["type"]]: SoupOps<K, AnySoupElementInput>
}

export type GetSoupUtilObject = ((soup: AnySoupElement[]) => SoupUtilObject) & {
unparsed: (soup: AnySoupElementInput[]) => SoupInputUtilObject
}

export const su: GetSoupUtilObject = ((soup: AnySoupElement[]) => {
const su = new Proxy(
{},
{
get: (proxy_target: any, component_type: string) => {
return {
get: (id: string) =>
soup.find(
(e: any) =>
e.type === component_type && e[`${component_type}_id`] === id
),
getUsing: (using: any) => {
const keys = Object.keys(using)
if (keys.length !== 1) {
throw new Error(
"getUsing requires exactly one key, e.g. { pcb_component_id }"
)
}
const join_key = keys[0] as string
const join_type = join_key.replace("_id", "")
const joiner: any = soup.find(
(e: any) =>
e.type === join_type && e[join_key] === using[join_key]
)
if (!joiner) return null
return soup.find(
(e: any) =>
e.type === component_type &&
e[`${component_type}_id`] === joiner[`${component_type}_id`]
)
},
getWhere: (where: any) => {
const keys = Object.keys(where)
return soup.find(
(e: any) =>
e.type === component_type &&
keys.every((key) => e[key] === where[key])
)
},
list: (where?: any) => {
const keys = !where ? [] : Object.keys(where)
return soup.filter(
(e: any) =>
e.type === component_type &&
keys.every((key) => e[key] === where[key])
)
},
select: (selector: string) => {
// TODO when applySelector is isolated we can use it, until then we
// do a poor man's selector implementation for two common cases
if (component_type === "source_component") {
return soup.find(
(e) =>
e.type === "source_component" &&
e.name === selector.replace(/\./g, "")
)
} else if (
component_type === "pcb_port" ||
component_type === "source_port" ||
component_type === "schematic_port"
) {
const [component_name, port_selector] = selector
.replace(/\./g, "")
.split(/[\s\>]+/)
const source_component = soup.find(
(e) =>
e.type === "source_component" && e.name === component_name
) as SourceComponentBase
if (!source_component) return null
const source_port = soup.find(
(e) =>
e.type === "source_port" &&
e.source_component_id ===
source_component.source_component_id &&
(e.name === port_selector ||
(e.port_hints ?? []).includes(port_selector!))
) as SourcePort
if (!source_port) return null
if (component_type === "source_port") return source_port

if (component_type === "pcb_port") {
return soup.find(
(e) =>
e.type === "pcb_port" &&
e.source_port_id === source_port.source_port_id
)
} else if (component_type === "schematic_port") {
return soup.find(
(e) =>
e.type === "schematic_port" &&
e.source_port_id === source_port.source_port_id
)
}
}
},
}
},
}
)

return su
}) as any
su.unparsed = su as any

export default su
Loading

0 comments on commit 6689228

Please sign in to comment.