Skip to content
Merged
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
42 changes: 40 additions & 2 deletions src/components/DimensionOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "lib/util/get-primitive-bounding-box"
import type { BoundingBox } from "lib/util/get-primitive-bounding-box"
import { useDiagonalLabel } from "hooks/useDiagonalLabel"
import { getPrimitiveSnapPoints } from "lib/util/get-primitive-snap-points"

interface Props {
transform?: Matrix
Expand Down Expand Up @@ -83,6 +84,13 @@ export const DimensionOverlay = ({
for (const primitive of primitives) {
if (!primitive._element) continue
if (shouldExcludePrimitiveFromSnapping(primitive)) continue
if (primitive.pcb_drawing_type === "pill") continue
if (
primitive.pcb_drawing_type === "rect" &&
primitive.ccw_rotation &&
primitive.ccw_rotation !== 0
)
continue
const bbox = getPrimitiveBoundingBox(primitive)
if (!bbox) continue

Expand All @@ -96,9 +104,35 @@ export const DimensionOverlay = ({
return boundingBoxes
}, [primitives])

const primitiveSnappingPoints = useMemo(() => {
const snapPoints: {
anchor: NinePointAnchor | string
point: { x: number; y: number }
element: object
}[] = []

for (const primitive of primitives) {
if (!primitive._element) continue
if (shouldExcludePrimitiveFromSnapping(primitive)) continue

const primitivePoints = getPrimitiveSnapPoints(primitive)
if (primitivePoints.length === 0) continue

for (const snap of primitivePoints) {
snapPoints.push({
anchor: snap.anchor,
point: snap.point,
element: primitive._element as object,
})
}
}

return snapPoints
}, [primitives])

const snappingPoints = useMemo(() => {
const points: {
anchor: NinePointAnchor | "origin"
anchor: NinePointAnchor | "origin" | string
point: { x: number; y: number }
element: object | null
}[] = []
Expand Down Expand Up @@ -133,14 +167,18 @@ export const DimensionOverlay = ({
}
})

for (const snap of primitiveSnappingPoints) {
points.push(snap)
}

points.push({
anchor: "origin",
point: { x: 0, y: 0 },
element: null,
})

return points
}, [elementBoundingBoxes])
}, [elementBoundingBoxes, primitiveSnappingPoints])

const snappingPointsWithScreen = useMemo(() => {
return snappingPoints.map((snap, index) => ({
Expand Down
151 changes: 151 additions & 0 deletions src/lib/util/get-primitive-snap-points.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import type { NinePointAnchor } from "circuit-json"
import type { Primitive } from "../types"

export interface PrimitiveSnapPoint {
anchor: NinePointAnchor | string
point: { x: number; y: number }
}

const rotatePoint = (
point: { x: number; y: number },
center: { x: number; y: number },
rotationDeg: number,
) => {
const radians = (rotationDeg * Math.PI) / 180
const cos = Math.cos(radians)
const sin = Math.sin(radians)

const translatedX = point.x - center.x
const translatedY = point.y - center.y

const rotatedX = translatedX * cos - translatedY * sin
const rotatedY = translatedX * sin + translatedY * cos

return {
x: rotatedX + center.x,
y: rotatedY + center.y,
}
}

const getNinePointAnchors = (
center: { x: number; y: number },
halfWidth: number,
halfHeight: number,
rotationDeg: number,
): PrimitiveSnapPoint[] => {
const basePoints: Record<NinePointAnchor, { x: number; y: number }> = {
top_left: { x: center.x - halfWidth, y: center.y - halfHeight },
top_center: { x: center.x, y: center.y - halfHeight },
top_right: { x: center.x + halfWidth, y: center.y - halfHeight },
center_left: { x: center.x - halfWidth, y: center.y },
center: { x: center.x, y: center.y },
center_right: { x: center.x + halfWidth, y: center.y },
bottom_left: { x: center.x - halfWidth, y: center.y + halfHeight },
bottom_center: { x: center.x, y: center.y + halfHeight },
bottom_right: { x: center.x + halfWidth, y: center.y + halfHeight },
}

if (rotationDeg === 0) {
return Object.entries(basePoints).map(([anchor, point]) => ({
anchor: anchor as NinePointAnchor,
point,
}))
}

return Object.entries(basePoints).map(([anchor, point]) => ({
anchor: anchor as NinePointAnchor,
point: rotatePoint(point, center, rotationDeg),
}))
}

export const getPrimitiveSnapPoints = (
primitive: Primitive,
): PrimitiveSnapPoint[] => {
switch (primitive.pcb_drawing_type) {
case "rect": {
const rotation = primitive.ccw_rotation ?? 0
return getNinePointAnchors(
{ x: primitive.x, y: primitive.y },
primitive.w / 2,
primitive.h / 2,
rotation,
)
}
case "pill": {
const rotation = primitive.ccw_rotation ?? 0
return getNinePointAnchors(
{ x: primitive.x, y: primitive.y },
primitive.w / 2,
primitive.h / 2,
rotation,
)
}
case "circle": {
return [
{ anchor: "circle_center", point: { x: primitive.x, y: primitive.y } },
{
anchor: "circle_right",
point: { x: primitive.x + primitive.r, y: primitive.y },
},
{
anchor: "circle_left",
point: { x: primitive.x - primitive.r, y: primitive.y },
},
{
anchor: "circle_top",
point: { x: primitive.x, y: primitive.y - primitive.r },
},
{
anchor: "circle_bottom",
point: { x: primitive.x, y: primitive.y + primitive.r },
},
]
}
case "oval": {
return [
{ anchor: "oval_center", point: { x: primitive.x, y: primitive.y } },
{
anchor: "oval_right",
point: { x: primitive.x + primitive.rX, y: primitive.y },
},
{
anchor: "oval_left",
point: { x: primitive.x - primitive.rX, y: primitive.y },
},
{
anchor: "oval_top",
point: { x: primitive.x, y: primitive.y - primitive.rY },
},
{
anchor: "oval_bottom",
point: { x: primitive.x, y: primitive.y + primitive.rY },
},
]
}
case "line": {
const midPoint = {
x: (primitive.x1 + primitive.x2) / 2,
y: (primitive.y1 + primitive.y2) / 2,
}
return [
{ anchor: "line_start", point: { x: primitive.x1, y: primitive.y1 } },
{ anchor: "line_mid", point: midPoint },
{ anchor: "line_end", point: { x: primitive.x2, y: primitive.y2 } },
]
}
case "polygon": {
return primitive.points.map((point, index) => ({
anchor: `polygon_vertex_${index}`,
point,
}))
}
case "polygon_with_arcs": {
return primitive.brep_shape.outer_ring.vertices.map((vertex, index) => ({
anchor: `polygon_with_arcs_vertex_${index}`,
point: { x: vertex.x, y: vertex.y },
}))
}
default:
return []
}
}