Skip to content

Geometry

geometry

DBUGeometricObject

Bases: GeometricObject[int], ABC

Source code in kfactory/geometry.py
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
class DBUGeometricObject(GeometricObject[int], ABC):
    def bbox(self, layer: int | None = None) -> kdb.Box:
        return self.ibbox(layer)

    def _standard_trans(self) -> type[kdb.Trans]:
        return kdb.Trans

    def rotate(self, angle: int, center: tuple[int, int] | None = None) -> Self:
        return self.irotate(angle, center)

    def mirror_x(self, x: int = 0) -> Self:
        return self.imirror_x(x)

    def mirror_y(self, y: int = 0) -> Self:
        return self.imirror_y(y)

    def mirror(
        self, p1: tuple[int, int] = (0, 1000), p2: tuple[int, int] = (0, 0)
    ) -> Self:
        return self.imirror(p1, p2)

mirror

mirror(
    p1: tuple[int, int] = (0, 1000),
    p2: tuple[int, int] = (0, 0),
) -> Self

Mirror self at a line.

Source code in kfactory/geometry.py
760
761
762
763
def mirror(
    self, p1: tuple[int, int] = (0, 1000), p2: tuple[int, int] = (0, 0)
) -> Self:
    return self.imirror(p1, p2)

mirror_x

mirror_x(x: int = 0) -> Self

Mirror self at an y-axis at position x.

Source code in kfactory/geometry.py
754
755
def mirror_x(self, x: int = 0) -> Self:
    return self.imirror_x(x)

mirror_y

mirror_y(y: int = 0) -> Self

Mirror self at an x-axis at position y.

Source code in kfactory/geometry.py
757
758
def mirror_y(self, y: int = 0) -> Self:
    return self.imirror_y(y)

rotate

rotate(
    angle: int, center: tuple[int, int] | None = None
) -> Self

Rotate self.

Source code in kfactory/geometry.py
751
752
def rotate(self, angle: int, center: tuple[int, int] | None = None) -> Self:
    return self.irotate(angle, center)

GeometricObject

Bases: ABC

Source code in kfactory/geometry.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
class GeometricObject[T: (int, float)](ABC):
    @property
    @abstractmethod
    def kcl(self) -> KCLayout: ...

    @abstractmethod
    def bbox(self, layer: int | None = None) -> BoxLike[T]: ...

    @abstractmethod
    def ibbox(self, layer: int | None = None) -> kdb.Box: ...

    @abstractmethod
    def dbbox(self, layer: int | None = None) -> kdb.DBox: ...

    @abstractmethod
    def _standard_trans(self) -> type[kdb.Trans | kdb.DCplxTrans]: ...

    @abstractmethod
    def transform(
        self,
        trans: kdb.Trans | kdb.DTrans | kdb.ICplxTrans | kdb.DCplxTrans,
        /,
    ) -> Any: ...

    @property
    def x(self) -> T:
        """Returns the x-coordinate of the center of the bounding box."""
        return self.bbox().center().x

    @x.setter
    def x(self, __val: T, /) -> None:
        """Moves self so that the bbox's center x-coordinate."""
        self.transform(self._standard_trans()(x=__val - self.bbox().center().x))  # ty:ignore[invalid-argument-type]

    @property
    def y(self) -> T:
        """Returns the y-coordinate of the center of the bounding box."""
        return self.bbox().center().y

    @y.setter
    def y(self, __val: T, /) -> None:
        """Moves self so that the bbox's center y-coordinate."""
        self.transform(self._standard_trans()(y=__val - self.bbox().center().y))  # ty:ignore[invalid-argument-type]

    @property
    def xmin(self) -> T:
        """Returns the x-coordinate of the left edge of the bounding box."""
        return self.bbox().left

    @xmin.setter
    def xmin(self, __val: T, /) -> None:
        """Moves self so that the bbox's left edge x-coordinate."""
        self.transform(self._standard_trans()(x=__val - self.bbox().left))  # ty:ignore[invalid-argument-type]

    @property
    def ymin(self) -> T:
        """Returns the y-coordinate of the bottom edge of the bounding box."""
        return self.bbox().bottom

    @ymin.setter
    def ymin(self, __val: T, /) -> None:
        """Moves self so that the bbox's bottom edge y-coordinate."""
        self.transform(self._standard_trans()(y=__val - self.bbox().bottom))  # ty:ignore[invalid-argument-type]

    @property
    def xmax(self) -> T:
        """Returns the x-coordinate of the right edge of the bounding box."""
        return self.bbox().right

    @xmax.setter
    def xmax(self, __val: T, /) -> None:
        """Moves self so that the bbox's right edge x-coordinate."""
        self.transform(self._standard_trans()(x=__val - self.bbox().right))  # ty:ignore[invalid-argument-type]

    @property
    def ymax(self) -> T:
        """Returns the y-coordinate of the top edge of the bounding box."""
        return self.bbox().top

    @ymax.setter
    def ymax(self, __val: T, /) -> None:
        """Moves self so that the bbox's top edge y-coordinate."""
        self.transform(self._standard_trans()(y=__val - self.bbox().top))  # ty:ignore[invalid-argument-type]

    @property
    def xsize(self) -> T:
        """Returns the width of the bounding box."""
        return self.bbox().width()

    @xsize.setter
    def xsize(self, __val: T, /) -> None:
        """Sets the width of the bounding box."""
        self.transform(self._standard_trans()(x=__val - self.bbox().width()))  # ty:ignore[invalid-argument-type]

    @property
    def ysize(self) -> T:
        """Returns the height of the bounding box."""
        return self.bbox().height()

    @ysize.setter
    def ysize(self, __val: T, /) -> None:
        """Sets the height of the bounding box."""
        self.transform(self._standard_trans()(y=__val - self.bbox().height()))  # ty:ignore[invalid-argument-type]

    @property
    def center(self) -> tuple[T, T]:
        """Returns the coordinate center of the bounding box."""
        center = self.bbox().center()
        return center.x, center.y

    @center.setter
    def center(self, __val: tuple[T, T], /) -> None:
        """Moves self so that the bbox's center coordinate."""
        self.transform(
            self._standard_trans()(
                __val[0] - self.bbox().center().x, __val[1] - self.bbox().center().y
            )  # ty:ignore[no-matching-overload]
        )

    @overload
    def move(self, destination: tuple[T, T], /) -> Self: ...

    @overload
    def move(self, origin: tuple[T, T], destination: tuple[T, T]) -> Self: ...

    def move(
        self,
        origin: tuple[T, T],
        destination: tuple[T, T] | None = None,
    ) -> Self:
        """Move self in dbu.

        Args:
            origin: reference point to move [dbu]
            destination: move origin so that it will land on this coordinate [dbu]
        """
        if destination is None:
            self.transform(self._standard_trans()(*origin))  # ty:ignore[no-matching-overload]
        else:
            self.transform(
                self._standard_trans()(
                    destination[0] - origin[0], destination[1] - origin[1]
                )  # ty:ignore[no-matching-overload]
            )
        return self

    @overload
    def movex(self, destination: T, /) -> Self: ...

    @overload
    def movex(self, origin: T, destination: T) -> Self: ...

    def movex(self, origin: T, destination: T | None = None) -> Self:
        """Move self in x-direction in dbu.

        Args:
            origin: reference point to move [dbu]
            destination: move origin so that it will land on this coordinate [dbu]
        """
        if destination is None:
            self.transform(self._standard_trans()(x=origin))  # ty:ignore[invalid-argument-type]
        else:
            self.transform(self._standard_trans()(x=destination - origin))  # ty:ignore[invalid-argument-type]
        return self

    @overload
    def movey(self, destination: T, /) -> Self: ...

    @overload
    def movey(self, origin: T, destination: T) -> Self: ...

    def movey(self, origin: T, destination: T | None = None) -> Self:
        """Move self in y-direction in dbu.

        Args:
            origin: reference point to move [dbu]
            destination: move origin so that it will land on this coordinate [dbu]
        """
        if destination is None:
            self.transform(self._standard_trans()(y=origin))  # ty:ignore[invalid-argument-type]
        else:
            self.transform(self._standard_trans()(y=destination - origin))  # ty:ignore[invalid-argument-type]
        return self

    @abstractmethod
    def rotate(self, angle: T, center: tuple[T, T] | None = None) -> Self:
        """Rotate self."""
        ...

    @abstractmethod
    def mirror(self, p1: tuple[T, T] = ..., p2: tuple[T, T] = ...) -> Self:
        """Mirror self at a line."""
        ...

    @abstractmethod
    def mirror_x(self, x: T = ...) -> Self:
        """Mirror self at an y-axis at position x."""
        ...

    @abstractmethod
    def mirror_y(self, y: T = ...) -> Self:
        """Mirror self at an x-axis at position y."""
        ...

    @property
    def ix(self) -> int:
        """Returns the x-coordinate of the center of the bounding box."""
        return self.ibbox().center().x

    @ix.setter
    def ix(self, __val: int, /) -> None:
        """Moves self so that the bbox's center x-coordinate."""
        self.transform(kdb.Trans(__val - self.ibbox().center().x, 0))

    @property
    def iy(self) -> int:
        """Returns the y-coordinate of the center of the bounding box."""
        return self.ibbox().center().y

    @iy.setter
    def iy(self, __val: int, /) -> None:
        """Moves self so that the bbox's center y-coordinate."""
        self.transform(kdb.Trans(0, __val - self.ibbox().center().y))

    @property
    def ixmin(self) -> int:
        """Returns the x-coordinate of the left edge of the bounding box."""
        return self.ibbox().left

    @ixmin.setter
    def ixmin(self, __val: int, /) -> None:
        """Moves self so that the bbox's left x-coordinate."""
        self.transform(kdb.Trans(__val - self.ibbox().left, 0))

    @property
    def iymin(self) -> int:
        """Returns the y-coordinate of the bottom edge of the bounding box."""
        return self.ibbox().bottom

    @iymin.setter
    def iymin(self, __val: int, /) -> None:
        """Moves self so that the bbox's bottom y-coordinate."""
        self.transform(kdb.Trans(0, __val - self.ibbox().bottom))

    @property
    def ixmax(self) -> int:
        """Returns the x-coordinate of the right edge of the bounding box."""
        return self.ibbox().right

    @ixmax.setter
    def ixmax(self, __val: int, /) -> None:
        """Moves self so that the bbox's right x-coordinate."""
        self.transform(kdb.Trans(__val - self.ibbox().right, 0))

    @property
    def iymax(self) -> int:
        """Returns the y-coordinate of the top edge of the bounding box."""
        return self.ibbox().top

    @iymax.setter
    def iymax(self, __val: int, /) -> None:
        """Moves self so that the bbox's top y-coordinate."""
        self.transform(kdb.Trans(0, __val - self.ibbox().top))

    @property
    def ixsize(self) -> int:
        """Returns the width of the bounding box."""
        return self.ibbox().width()

    @ixsize.setter
    def ixsize(self, __val: int, /) -> None:
        """Sets the width of the bounding box."""
        self.transform(kdb.Trans(__val - self.ibbox().width(), 0))

    @property
    def iysize(self) -> int:
        """Returns the height of the bounding box."""
        return self.ibbox().height()

    @iysize.setter
    def iysize(self, __val: int, /) -> None:
        """Sets the height of the bounding box."""
        self.transform(kdb.Trans(0, __val - self.ibbox().height()))

    @property
    def icenter(self) -> tuple[int, int]:
        """Returns the coordinate center of the bounding box."""
        center = self.ibbox().center()
        return center.x, center.y

    @icenter.setter
    def icenter(self, val: tuple[int, int]) -> None:
        """Moves self so that the bbox's center coordinate."""
        self.transform(
            kdb.Trans(
                val[0] - self.ibbox().center().x, val[1] - self.ibbox().center().y
            )
        )

    @overload
    def imove(self, destination: tuple[int, int], /) -> Self: ...

    @overload
    def imove(
        self, origin: tuple[int, int], destination: tuple[int, int] | None = None
    ) -> Self: ...

    def imove(
        self, origin: tuple[int, int], destination: tuple[int, int] | None = None
    ) -> Self:
        """Move self in dbu.

        Args:
            origin: reference point to move [dbu]
            destination: move origin so that it will land on this coordinate [dbu]
        """
        if destination is None:
            self.transform(kdb.Trans(*origin))
        else:
            self.transform(
                kdb.Trans(destination[0] - origin[0], destination[1] - origin[1])
            )
        return self

    @overload
    def imovex(self, destination: int, /) -> Self: ...

    @overload
    def imovex(self, origin: int, destination: int | None = None) -> Self: ...

    def imovex(self, origin: int, destination: int | None = None) -> Self:
        """Move self in x-direction in dbu.

        Args:
            origin: reference point to move [dbu]
            destination: move origin so that it will land on this coordinate [dbu]
        """
        if destination is None:
            self.transform(kdb.Trans(origin, 0))
        else:
            self.transform(kdb.Trans(destination - origin, 0))
        return self

    @overload
    def imovey(self, destination: int, /) -> Self: ...

    @overload
    def imovey(self, origin: int, destination: int | None = None) -> Self: ...

    def imovey(self, origin: int, destination: int | None = None) -> Self:
        """Move self in y-direction in dbu.

        Args:
            origin: reference point to move [dbu]
            destination: move origin so that it will land on this coordinate [dbu]
        """
        if destination is None:
            self.transform(kdb.Trans(0, origin))
        else:
            self.transform(kdb.Trans(0, destination - origin))
        return self

    def irotate(self, angle: int, center: tuple[int, int] | None = None) -> Self:
        """Rotate self in increments of 90°."""
        t: kdb.Trans | None = None
        if center:
            t = kdb.Trans(*center)
            self.transform(t.inverted())
        self.transform(kdb.Trans(rot=angle, mirrx=False, x=0, y=0))
        if center and t:
            self.transform(t)
        return self

    def imirror(
        self, p1: tuple[int, int] = (0, 1000), p2: tuple[int, int] = (0, 0)
    ) -> Self:
        """Mirror self at a line."""
        p1_ = kdb.Point(p1[0], p1[1]).to_dtype(self.kcl.dbu)
        p2_ = kdb.Point(p2[0], p2[1]).to_dtype(self.kcl.dbu)
        mirror_v = p2_ - p1_
        disp = kdb.DVector(self.dxmin, self.dymin)
        angle = np.mod(np.rad2deg(np.arctan2(mirror_v.y, mirror_v.x)), 180) * 2
        dedge = kdb.DEdge(p1_, p2_)

        v = kdb.DVector(-mirror_v.y, mirror_v.x)

        dedge_disp = kdb.DEdge(disp.to_p(), (v + disp).to_p())

        cross_point = dedge.cut_point(dedge_disp)

        self.transform(
            kdb.DCplxTrans(1.0, angle, True, (cross_point.to_v() - disp) * 2)
        )

        return self

    def imirror_x(self, x: int = 0) -> Self:
        """Mirror self at an y-axis at position x."""
        self.transform(kdb.Trans(2, True, 2 * x, 0))
        return self

    def imirror_y(self, y: int = 0) -> Self:
        """Mirror self at an x-axis at position y."""
        self.transform(kdb.Trans(0, True, 0, 2 * y))
        return self

    @property
    def dx(self) -> float:
        """X coordinate of the center of the bounding box in um."""
        return self.dbbox().center().x

    @dx.setter
    def dx(self, __val: float, /) -> None:
        """Moves self so that the bbox's center x-coordinate in um."""
        self.transform(kdb.DTrans(__val - self.dbbox().center().x, 0))

    @property
    def dy(self) -> float:
        """Y coordinate of the center of the bounding box in um."""
        return self.dbbox().center().y

    @dy.setter
    def dy(self, __val: float, /) -> None:
        """Moves self so that the bbox's center y-coordinate in um."""
        self.transform(kdb.DTrans(0, __val - self.dbbox().center().y))

    @property
    def dxmin(self) -> float:
        """Returns the x-coordinate of the left edge of the bounding box."""
        return self.dbbox().left

    @dxmin.setter
    def dxmin(self, __val: float, /) -> None:
        """Moves self so that the bbox's left x-coordinate in um."""
        self.transform(kdb.DTrans(__val - self.dbbox().left, 0))

    @property
    def dymin(self) -> float:
        """Returns the y-coordinate of the bottom edge of the bounding box."""
        return self.dbbox().bottom

    @dymin.setter
    def dymin(self, __val: float, /) -> None:
        """Moves self so that the bbox's bottom y-coordinate in um."""
        self.transform(kdb.DTrans(0, __val - self.dbbox().bottom))

    @property
    def dxmax(self) -> float:
        """Returns the x-coordinate of the right edge of the bounding box."""
        return self.dbbox().right

    @dxmax.setter
    def dxmax(self, __val: float, /) -> None:
        """Moves self so that the bbox's right x-coordinate in um."""
        self.transform(kdb.DTrans(__val - self.dbbox().right, 0))

    @property
    def dymax(self) -> float:
        """Returns the y-coordinate of the top edge of the bounding box."""
        return self.dbbox().top

    @dymax.setter
    def dymax(self, __val: float, /) -> None:
        """Moves self so that the bbox's top y-coordinate in um."""
        self.transform(kdb.DTrans(0, __val - self.dbbox().top))

    @property
    def dxsize(self) -> float:
        """Returns the width of the bounding box."""
        return self.dbbox().width()

    @dxsize.setter
    def dxsize(self, __val: float, /) -> None:
        """Sets the width of the bounding box in um."""
        self.transform(kdb.DTrans(__val - self.dbbox().width(), 0))

    @property
    def dysize(self) -> float:
        """Returns the height of the bounding box."""
        return self.dbbox().height()

    @dysize.setter
    def dysize(self, __val: float, /) -> None:
        """Sets the height of the bounding box in um."""
        self.transform(kdb.DTrans(0, __val - self.dbbox().height()))

    @property
    def dcenter(self) -> tuple[float, float]:
        """Coordinate of the center of the bounding box in um."""
        center = self.dbbox().center()
        return center.x, center.y

    @dcenter.setter
    def dcenter(self, val: tuple[float, float]) -> None:
        """Moves self so that the bbox's center coordinate in um."""
        self.transform(
            kdb.DTrans(
                val[0] - self.dbbox().center().x, val[1] - self.dbbox().center().y
            )
        )

    @overload
    def dmove(self, destination: tuple[float, float], /) -> Self: ...

    @overload
    def dmove(
        self,
        origin: tuple[float, float],
        destination: tuple[float, float] | None = None,
    ) -> Self: ...

    def dmove(
        self,
        origin: tuple[float, float],
        destination: tuple[float, float] | None = None,
    ) -> Self:
        """Move self in um.

        Args:
            origin: reference point to move
            destination: move origin so that it will land on this coordinate
        """
        if destination is None:
            self.transform(kdb.DCplxTrans(*origin))
        else:
            self.transform(
                kdb.DCplxTrans(destination[0] - origin[0], destination[1] - origin[1])
            )
        return self

    @overload
    def dmovex(self, destination: float, /) -> Self: ...

    @overload
    def dmovex(self, origin: float, destination: float | None = None) -> Self: ...

    def dmovex(self, origin: float, destination: float | None = None) -> Self:
        """Move self in x-direction in um.

        Args:
            origin: reference point to move
            destination: move origin so that it will land on this coordinate
        """
        if destination is None:
            self.transform(kdb.DCplxTrans(origin, 0))
        else:
            self.transform(kdb.DCplxTrans(destination - origin, 0))
        return self

    @overload
    def dmovey(self, destination: float, /) -> Self: ...

    @overload
    def dmovey(self, origin: float, destination: float | None = None) -> Self: ...

    def dmovey(self, origin: float, destination: float | None = None) -> Self:
        """Move self in y-direction in um.

        Args:
            origin: reference point to move
            destination: move origin so that it will land on this coordinate
        """
        if destination is None:
            self.transform(kdb.DCplxTrans(0, origin))
        else:
            self.transform(kdb.DCplxTrans(0, destination - origin))
        return self

    def drotate(self, angle: float, center: tuple[float, float] | None = None) -> Self:
        """Rotate self by a given angle in degrees.

        Args:
            angle: angle to rotate self
            center: reference point to rotate around
        """
        t: kdb.DCplxTrans | None = None
        if center:
            t = kdb.DCplxTrans(*center)
            self.transform(t.inverted())
        self.transform(kdb.DCplxTrans(rot=angle, mirrx=False, x=0, y=0))
        if center and t:
            self.transform(t)
        return self

    def dmirror(
        self, p1: tuple[float, float] = (0, 1), p2: tuple[float, float] = (0, 0)
    ) -> Self:
        """Mirror self at a line."""
        p1_ = kdb.DPoint(p1[0], p1[1])
        p2_ = kdb.DPoint(p2[0], p2[1])
        mirror_v = p2_ - p1_
        disp = kdb.DVector(self.dxmin, self.dymin)
        angle = np.mod(np.rad2deg(np.arctan2(mirror_v.y, mirror_v.x)), 180) * 2
        dedge = kdb.DEdge(p1_, p2_)

        v = mirror_v
        v = kdb.DVector(-v.y, v.x)

        dedge_disp = kdb.DEdge(disp.to_p(), (v + disp).to_p())

        cross_point = dedge.cut_point(dedge_disp)

        self.transform(
            kdb.DCplxTrans(1.0, angle, True, (cross_point.to_v() - disp) * 2)
        )

        return self

    def dmirror_x(self, x: float = 0) -> Self:
        """Mirror self at an y-axis at position x."""
        self.transform(kdb.DCplxTrans(1, 180, True, 2 * x, 0))
        return self

    def dmirror_y(self, y: float = 0) -> Self:
        """Mirror self at an x-axis at position y."""
        self.transform(kdb.DCplxTrans(1, 0, True, 0, 2 * y))
        return self

    @property
    def size_info(self) -> SizeInfo[T]:
        return SizeInfo[T](self.bbox)

    @property
    def isize_info(self) -> SizeInfo[int]:
        return SizeInfo[int](self.ibbox)

    @property
    def dsize_info(self) -> SizeInfo[float]:
        return SizeInfo[float](self.dbbox)

center property writable

center: tuple[T, T]

Returns the coordinate center of the bounding box.

dcenter property writable

dcenter: tuple[float, float]

Coordinate of the center of the bounding box in um.

dx property writable

dx: float

X coordinate of the center of the bounding box in um.

dxmax property writable

dxmax: float

Returns the x-coordinate of the right edge of the bounding box.

dxmin property writable

dxmin: float

Returns the x-coordinate of the left edge of the bounding box.

dxsize property writable

dxsize: float

Returns the width of the bounding box.

dy property writable

dy: float

Y coordinate of the center of the bounding box in um.

dymax property writable

dymax: float

Returns the y-coordinate of the top edge of the bounding box.

dymin property writable

dymin: float

Returns the y-coordinate of the bottom edge of the bounding box.

dysize property writable

dysize: float

Returns the height of the bounding box.

icenter property writable

icenter: tuple[int, int]

Returns the coordinate center of the bounding box.

ix property writable

ix: int

Returns the x-coordinate of the center of the bounding box.

ixmax property writable

ixmax: int

Returns the x-coordinate of the right edge of the bounding box.

ixmin property writable

ixmin: int

Returns the x-coordinate of the left edge of the bounding box.

ixsize property writable

ixsize: int

Returns the width of the bounding box.

iy property writable

iy: int

Returns the y-coordinate of the center of the bounding box.

iymax property writable

iymax: int

Returns the y-coordinate of the top edge of the bounding box.

iymin property writable

iymin: int

Returns the y-coordinate of the bottom edge of the bounding box.

iysize property writable

iysize: int

Returns the height of the bounding box.

x property writable

x: T

Returns the x-coordinate of the center of the bounding box.

xmax property writable

xmax: T

Returns the x-coordinate of the right edge of the bounding box.

xmin property writable

xmin: T

Returns the x-coordinate of the left edge of the bounding box.

xsize property writable

xsize: T

Returns the width of the bounding box.

y property writable

y: T

Returns the y-coordinate of the center of the bounding box.

ymax property writable

ymax: T

Returns the y-coordinate of the top edge of the bounding box.

ymin property writable

ymin: T

Returns the y-coordinate of the bottom edge of the bounding box.

ysize property writable

ysize: T

Returns the height of the bounding box.

dmirror

dmirror(
    p1: tuple[float, float] = (0, 1),
    p2: tuple[float, float] = (0, 0),
) -> Self

Mirror self at a line.

Source code in kfactory/geometry.py
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
def dmirror(
    self, p1: tuple[float, float] = (0, 1), p2: tuple[float, float] = (0, 0)
) -> Self:
    """Mirror self at a line."""
    p1_ = kdb.DPoint(p1[0], p1[1])
    p2_ = kdb.DPoint(p2[0], p2[1])
    mirror_v = p2_ - p1_
    disp = kdb.DVector(self.dxmin, self.dymin)
    angle = np.mod(np.rad2deg(np.arctan2(mirror_v.y, mirror_v.x)), 180) * 2
    dedge = kdb.DEdge(p1_, p2_)

    v = mirror_v
    v = kdb.DVector(-v.y, v.x)

    dedge_disp = kdb.DEdge(disp.to_p(), (v + disp).to_p())

    cross_point = dedge.cut_point(dedge_disp)

    self.transform(
        kdb.DCplxTrans(1.0, angle, True, (cross_point.to_v() - disp) * 2)
    )

    return self

dmirror_x

dmirror_x(x: float = 0) -> Self

Mirror self at an y-axis at position x.

Source code in kfactory/geometry.py
721
722
723
724
def dmirror_x(self, x: float = 0) -> Self:
    """Mirror self at an y-axis at position x."""
    self.transform(kdb.DCplxTrans(1, 180, True, 2 * x, 0))
    return self

dmirror_y

dmirror_y(y: float = 0) -> Self

Mirror self at an x-axis at position y.

Source code in kfactory/geometry.py
726
727
728
729
def dmirror_y(self, y: float = 0) -> Self:
    """Mirror self at an x-axis at position y."""
    self.transform(kdb.DCplxTrans(1, 0, True, 0, 2 * y))
    return self

dmove

dmove(destination: tuple[float, float]) -> Self
dmove(
    origin: tuple[float, float],
    destination: tuple[float, float] | None = None,
) -> Self
dmove(
    origin: tuple[float, float],
    destination: tuple[float, float] | None = None,
) -> Self

Move self in um.

Parameters:

Name Type Description Default
origin tuple[float, float]

reference point to move

required
destination tuple[float, float] | None

move origin so that it will land on this coordinate

None
Source code in kfactory/geometry.py
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
def dmove(
    self,
    origin: tuple[float, float],
    destination: tuple[float, float] | None = None,
) -> Self:
    """Move self in um.

    Args:
        origin: reference point to move
        destination: move origin so that it will land on this coordinate
    """
    if destination is None:
        self.transform(kdb.DCplxTrans(*origin))
    else:
        self.transform(
            kdb.DCplxTrans(destination[0] - origin[0], destination[1] - origin[1])
        )
    return self

dmovex

dmovex(destination: float) -> Self
dmovex(
    origin: float, destination: float | None = None
) -> Self
dmovex(
    origin: float, destination: float | None = None
) -> Self

Move self in x-direction in um.

Parameters:

Name Type Description Default
origin float

reference point to move

required
destination float | None

move origin so that it will land on this coordinate

None
Source code in kfactory/geometry.py
649
650
651
652
653
654
655
656
657
658
659
660
def dmovex(self, origin: float, destination: float | None = None) -> Self:
    """Move self in x-direction in um.

    Args:
        origin: reference point to move
        destination: move origin so that it will land on this coordinate
    """
    if destination is None:
        self.transform(kdb.DCplxTrans(origin, 0))
    else:
        self.transform(kdb.DCplxTrans(destination - origin, 0))
    return self

dmovey

dmovey(destination: float) -> Self
dmovey(
    origin: float, destination: float | None = None
) -> Self
dmovey(
    origin: float, destination: float | None = None
) -> Self

Move self in y-direction in um.

Parameters:

Name Type Description Default
origin float

reference point to move

required
destination float | None

move origin so that it will land on this coordinate

None
Source code in kfactory/geometry.py
668
669
670
671
672
673
674
675
676
677
678
679
def dmovey(self, origin: float, destination: float | None = None) -> Self:
    """Move self in y-direction in um.

    Args:
        origin: reference point to move
        destination: move origin so that it will land on this coordinate
    """
    if destination is None:
        self.transform(kdb.DCplxTrans(0, origin))
    else:
        self.transform(kdb.DCplxTrans(0, destination - origin))
    return self

drotate

drotate(
    angle: float, center: tuple[float, float] | None = None
) -> Self

Rotate self by a given angle in degrees.

Parameters:

Name Type Description Default
angle float

angle to rotate self

required
center tuple[float, float] | None

reference point to rotate around

None
Source code in kfactory/geometry.py
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
def drotate(self, angle: float, center: tuple[float, float] | None = None) -> Self:
    """Rotate self by a given angle in degrees.

    Args:
        angle: angle to rotate self
        center: reference point to rotate around
    """
    t: kdb.DCplxTrans | None = None
    if center:
        t = kdb.DCplxTrans(*center)
        self.transform(t.inverted())
    self.transform(kdb.DCplxTrans(rot=angle, mirrx=False, x=0, y=0))
    if center and t:
        self.transform(t)
    return self

imirror

imirror(
    p1: tuple[int, int] = (0, 1000),
    p2: tuple[int, int] = (0, 0),
) -> Self

Mirror self at a line.

Source code in kfactory/geometry.py
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
def imirror(
    self, p1: tuple[int, int] = (0, 1000), p2: tuple[int, int] = (0, 0)
) -> Self:
    """Mirror self at a line."""
    p1_ = kdb.Point(p1[0], p1[1]).to_dtype(self.kcl.dbu)
    p2_ = kdb.Point(p2[0], p2[1]).to_dtype(self.kcl.dbu)
    mirror_v = p2_ - p1_
    disp = kdb.DVector(self.dxmin, self.dymin)
    angle = np.mod(np.rad2deg(np.arctan2(mirror_v.y, mirror_v.x)), 180) * 2
    dedge = kdb.DEdge(p1_, p2_)

    v = kdb.DVector(-mirror_v.y, mirror_v.x)

    dedge_disp = kdb.DEdge(disp.to_p(), (v + disp).to_p())

    cross_point = dedge.cut_point(dedge_disp)

    self.transform(
        kdb.DCplxTrans(1.0, angle, True, (cross_point.to_v() - disp) * 2)
    )

    return self

imirror_x

imirror_x(x: int = 0) -> Self

Mirror self at an y-axis at position x.

Source code in kfactory/geometry.py
509
510
511
512
def imirror_x(self, x: int = 0) -> Self:
    """Mirror self at an y-axis at position x."""
    self.transform(kdb.Trans(2, True, 2 * x, 0))
    return self

imirror_y

imirror_y(y: int = 0) -> Self

Mirror self at an x-axis at position y.

Source code in kfactory/geometry.py
514
515
516
517
def imirror_y(self, y: int = 0) -> Self:
    """Mirror self at an x-axis at position y."""
    self.transform(kdb.Trans(0, True, 0, 2 * y))
    return self

imove

imove(destination: tuple[int, int]) -> Self
imove(
    origin: tuple[int, int],
    destination: tuple[int, int] | None = None,
) -> Self
imove(
    origin: tuple[int, int],
    destination: tuple[int, int] | None = None,
) -> Self

Move self in dbu.

Parameters:

Name Type Description Default
origin tuple[int, int]

reference point to move [dbu]

required
destination tuple[int, int] | None

move origin so that it will land on this coordinate [dbu]

None
Source code in kfactory/geometry.py
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
def imove(
    self, origin: tuple[int, int], destination: tuple[int, int] | None = None
) -> Self:
    """Move self in dbu.

    Args:
        origin: reference point to move [dbu]
        destination: move origin so that it will land on this coordinate [dbu]
    """
    if destination is None:
        self.transform(kdb.Trans(*origin))
    else:
        self.transform(
            kdb.Trans(destination[0] - origin[0], destination[1] - origin[1])
        )
    return self

imovex

imovex(destination: int) -> Self
imovex(origin: int, destination: int | None = None) -> Self
imovex(origin: int, destination: int | None = None) -> Self

Move self in x-direction in dbu.

Parameters:

Name Type Description Default
origin int

reference point to move [dbu]

required
destination int | None

move origin so that it will land on this coordinate [dbu]

None
Source code in kfactory/geometry.py
443
444
445
446
447
448
449
450
451
452
453
454
def imovex(self, origin: int, destination: int | None = None) -> Self:
    """Move self in x-direction in dbu.

    Args:
        origin: reference point to move [dbu]
        destination: move origin so that it will land on this coordinate [dbu]
    """
    if destination is None:
        self.transform(kdb.Trans(origin, 0))
    else:
        self.transform(kdb.Trans(destination - origin, 0))
    return self

imovey

imovey(destination: int) -> Self
imovey(origin: int, destination: int | None = None) -> Self
imovey(origin: int, destination: int | None = None) -> Self

Move self in y-direction in dbu.

Parameters:

Name Type Description Default
origin int

reference point to move [dbu]

required
destination int | None

move origin so that it will land on this coordinate [dbu]

None
Source code in kfactory/geometry.py
462
463
464
465
466
467
468
469
470
471
472
473
def imovey(self, origin: int, destination: int | None = None) -> Self:
    """Move self in y-direction in dbu.

    Args:
        origin: reference point to move [dbu]
        destination: move origin so that it will land on this coordinate [dbu]
    """
    if destination is None:
        self.transform(kdb.Trans(0, origin))
    else:
        self.transform(kdb.Trans(0, destination - origin))
    return self

irotate

irotate(
    angle: int, center: tuple[int, int] | None = None
) -> Self

Rotate self in increments of 90°.

Source code in kfactory/geometry.py
475
476
477
478
479
480
481
482
483
484
def irotate(self, angle: int, center: tuple[int, int] | None = None) -> Self:
    """Rotate self in increments of 90°."""
    t: kdb.Trans | None = None
    if center:
        t = kdb.Trans(*center)
        self.transform(t.inverted())
    self.transform(kdb.Trans(rot=angle, mirrx=False, x=0, y=0))
    if center and t:
        self.transform(t)
    return self

mirror abstractmethod

mirror(
    p1: tuple[T, T] = ..., p2: tuple[T, T] = ...
) -> Self

Mirror self at a line.

Source code in kfactory/geometry.py
302
303
304
305
@abstractmethod
def mirror(self, p1: tuple[T, T] = ..., p2: tuple[T, T] = ...) -> Self:
    """Mirror self at a line."""
    ...

mirror_x abstractmethod

mirror_x(x: T = ...) -> Self

Mirror self at an y-axis at position x.

Source code in kfactory/geometry.py
307
308
309
310
@abstractmethod
def mirror_x(self, x: T = ...) -> Self:
    """Mirror self at an y-axis at position x."""
    ...

mirror_y abstractmethod

mirror_y(y: T = ...) -> Self

Mirror self at an x-axis at position y.

Source code in kfactory/geometry.py
312
313
314
315
@abstractmethod
def mirror_y(self, y: T = ...) -> Self:
    """Mirror self at an x-axis at position y."""
    ...

move

move(destination: tuple[T, T]) -> Self
move(origin: tuple[T, T], destination: tuple[T, T]) -> Self
move(
    origin: tuple[T, T],
    destination: tuple[T, T] | None = None,
) -> Self

Move self in dbu.

Parameters:

Name Type Description Default
origin tuple[T, T]

reference point to move [dbu]

required
destination tuple[T, T] | None

move origin so that it will land on this coordinate [dbu]

None
Source code in kfactory/geometry.py
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def move(
    self,
    origin: tuple[T, T],
    destination: tuple[T, T] | None = None,
) -> Self:
    """Move self in dbu.

    Args:
        origin: reference point to move [dbu]
        destination: move origin so that it will land on this coordinate [dbu]
    """
    if destination is None:
        self.transform(self._standard_trans()(*origin))  # ty:ignore[no-matching-overload]
    else:
        self.transform(
            self._standard_trans()(
                destination[0] - origin[0], destination[1] - origin[1]
            )  # ty:ignore[no-matching-overload]
        )
    return self

movex

movex(destination: T) -> Self
movex(origin: T, destination: T) -> Self
movex(origin: T, destination: T | None = None) -> Self

Move self in x-direction in dbu.

Parameters:

Name Type Description Default
origin T

reference point to move [dbu]

required
destination T | None

move origin so that it will land on this coordinate [dbu]

None
Source code in kfactory/geometry.py
265
266
267
268
269
270
271
272
273
274
275
276
def movex(self, origin: T, destination: T | None = None) -> Self:
    """Move self in x-direction in dbu.

    Args:
        origin: reference point to move [dbu]
        destination: move origin so that it will land on this coordinate [dbu]
    """
    if destination is None:
        self.transform(self._standard_trans()(x=origin))  # ty:ignore[invalid-argument-type]
    else:
        self.transform(self._standard_trans()(x=destination - origin))  # ty:ignore[invalid-argument-type]
    return self

movey

movey(destination: T) -> Self
movey(origin: T, destination: T) -> Self
movey(origin: T, destination: T | None = None) -> Self

Move self in y-direction in dbu.

Parameters:

Name Type Description Default
origin T

reference point to move [dbu]

required
destination T | None

move origin so that it will land on this coordinate [dbu]

None
Source code in kfactory/geometry.py
284
285
286
287
288
289
290
291
292
293
294
295
def movey(self, origin: T, destination: T | None = None) -> Self:
    """Move self in y-direction in dbu.

    Args:
        origin: reference point to move [dbu]
        destination: move origin so that it will land on this coordinate [dbu]
    """
    if destination is None:
        self.transform(self._standard_trans()(y=origin))  # ty:ignore[invalid-argument-type]
    else:
        self.transform(self._standard_trans()(y=destination - origin))  # ty:ignore[invalid-argument-type]
    return self

rotate abstractmethod

rotate(angle: T, center: tuple[T, T] | None = None) -> Self

Rotate self.

Source code in kfactory/geometry.py
297
298
299
300
@abstractmethod
def rotate(self, angle: T, center: tuple[T, T] | None = None) -> Self:
    """Rotate self."""
    ...

SizeInfo

Source code in kfactory/geometry.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
class SizeInfo[T: (int, float)]:
    _bf: BoxFunction[T]

    def __init__(self, bbox: BoxFunction[T]) -> None:
        """Initialize this object."""
        super().__init__()
        self._bf = bbox

    def __str__(self) -> str:
        return (
            f"SizeInfo: {self.width=}, {self.height=}, {self.west=}, {self.east=},"
            f" {self.south=}, {self.north=}"
        )

    def __call__(self, layer: int | LayerEnum) -> SizeInfo[T]:
        def layer_bbox() -> BoxLike[T]:
            return self._bf(layer)

        return SizeInfo[T](bbox=layer_bbox)  # ty:ignore[invalid-argument-type]

    @property
    def west(self) -> T:
        return self._bf().left

    @property
    def east(self) -> T:
        return self._bf().right

    @property
    def south(self) -> T:
        return self._bf().bottom

    @property
    def north(self) -> T:
        return self._bf().top

    @property
    def width(self) -> T:
        return self._bf().width()

    @property
    def height(self) -> T:
        return self._bf().height()

    @property
    def sw(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.left, bb.bottom)

    @property
    def nw(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.left, bb.top)

    @property
    def se(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.right, bb.bottom)

    @property
    def ne(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.right, bb.top)

    @property
    def cw(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.left, bb.center().y)

    @property
    def ce(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.right, bb.center().y)

    @property
    def sc(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.center().x, bb.bottom)

    @property
    def nc(self) -> tuple[T, T]:
        bb = self._bf()
        return (bb.center().x, bb.top)

    @property
    def cc(self) -> tuple[T, T]:
        c = self._bf().center()
        return (c.x, c.y)

    @property
    def center(self) -> tuple[T, T]:
        c = self._bf().center()
        return (c.x, c.y)

__init__

__init__(bbox: BoxFunction[T]) -> None

Initialize this object.

Source code in kfactory/geometry.py
21
22
23
24
def __init__(self, bbox: BoxFunction[T]) -> None:
    """Initialize this object."""
    super().__init__()
    self._bf = bbox

UMGeometricObject

Bases: GeometricObject[float], ABC

Source code in kfactory/geometry.py
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
class UMGeometricObject(GeometricObject[float], ABC):
    def bbox(self, layer: int | None = None) -> kdb.DBox:
        return self.dbbox(layer)

    def _standard_trans(self) -> type[kdb.DCplxTrans]:
        return kdb.DCplxTrans

    def rotate(self, angle: float, center: tuple[float, float] | None = None) -> Self:
        return self.drotate(angle, center)

    def mirror_x(self, x: float = 0) -> Self:
        return self.dmirror_x(x)

    def mirror_y(self, y: float = 0) -> Self:
        return self.dmirror_y(y)

    def mirror(
        self, p1: tuple[float, float] = (0, 1), p2: tuple[float, float] = (0, 0)
    ) -> Self:
        return self.dmirror(p1, p2)

mirror

mirror(
    p1: tuple[float, float] = (0, 1),
    p2: tuple[float, float] = (0, 0),
) -> Self

Mirror self at a line.

Source code in kfactory/geometry.py
782
783
784
785
def mirror(
    self, p1: tuple[float, float] = (0, 1), p2: tuple[float, float] = (0, 0)
) -> Self:
    return self.dmirror(p1, p2)

mirror_x

mirror_x(x: float = 0) -> Self

Mirror self at an y-axis at position x.

Source code in kfactory/geometry.py
776
777
def mirror_x(self, x: float = 0) -> Self:
    return self.dmirror_x(x)

mirror_y

mirror_y(y: float = 0) -> Self

Mirror self at an x-axis at position y.

Source code in kfactory/geometry.py
779
780
def mirror_y(self, y: float = 0) -> Self:
    return self.dmirror_y(y)

rotate

rotate(
    angle: float, center: tuple[float, float] | None = None
) -> Self

Rotate self.

Source code in kfactory/geometry.py
773
774
def rotate(self, angle: float, center: tuple[float, float] | None = None) -> Self:
    return self.drotate(angle, center)