altium-toolkit 1.0.1 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ const { createSvgText, escapeHtml, formatNumber, projectSchematicY } =
15
15
  export class SchematicPowerPortRenderer {
16
16
  /**
17
17
  * Renders one power-port symbol and label.
18
- * @param {{ x: number, y: number, text: string, color: string, style?: number, fontSize?: number, fontFamily?: string, fontWeight?: number, anchor?: 'start' | 'middle' | 'end', powerPortDirection?: 'up' | 'down' | 'left' | 'right' }} text
18
+ * @param {{ x: number, y: number, text: string, color: string, style?: number, fontSize?: number, fontFamily?: string, fontWeight?: number, fontStyle?: string, anchor?: 'start' | 'middle' | 'end', powerPortDirection?: 'up' | 'down' | 'left' | 'right' }} text
19
19
  * @param {{ x1: number, y1: number, x2: number, y2: number }[]} lines
20
20
  * @param {{ x: number, y: number, length: number, orientation: 'left' | 'right' | 'top' | 'bottom' }[]} pins
21
21
  * @param {number} sheetHeight
@@ -32,7 +32,8 @@ export class SchematicPowerPortRenderer {
32
32
  const labelOptions = SchematicTypography.withViewerFontSize({
33
33
  fontSize: Number(text.fontSize || 10),
34
34
  fontFamily: text.fontFamily,
35
- fontWeight: text.fontWeight
35
+ fontWeight: text.fontWeight,
36
+ fontStyle: text.fontStyle
36
37
  })
37
38
  const fontSize = Number(labelOptions.fontSize || 9)
38
39
  const resolvedColor = SchematicColorResolver.resolveColor(
@@ -56,9 +57,7 @@ export class SchematicPowerPortRenderer {
56
57
  y,
57
58
  fontSize,
58
59
  labelOptions,
59
- resolvedColor,
60
- lines,
61
- sheetHeight
60
+ resolvedColor
62
61
  ) +
63
62
  '</g>'
64
63
  )
@@ -66,7 +65,7 @@ export class SchematicPowerPortRenderer {
66
65
 
67
66
  return (
68
67
  '<g class="schematic-power-port schematic-power-port--rail" stroke-linecap="round">' +
69
- SchematicPowerPortRenderer.#buildRailLine(
68
+ SchematicPowerPortRenderer.#buildRailLines(
70
69
  x,
71
70
  y,
72
71
  direction,
@@ -79,9 +78,7 @@ export class SchematicPowerPortRenderer {
79
78
  y,
80
79
  fontSize,
81
80
  labelOptions,
82
- resolvedColor,
83
- lines,
84
- sheetHeight
81
+ resolvedColor
85
82
  ) +
86
83
  '</g>'
87
84
  )
@@ -487,12 +484,18 @@ export class SchematicPowerPortRenderer {
487
484
  * @param {string} color
488
485
  * @returns {string}
489
486
  */
490
- static #buildRailLine(x, y, direction, color) {
487
+ static #buildRailLines(x, y, direction, color) {
491
488
  const stroke = escapeHtml(color)
492
- const x2 =
489
+ const stemX2 =
493
490
  direction === 'left' ? x - 12 : direction === 'right' ? x + 12 : x
494
- const y2 =
491
+ const stemY2 =
495
492
  direction === 'up' ? y - 12 : direction === 'down' ? y + 12 : y
493
+ const capLine = SchematicPowerPortRenderer.#buildRailCapLine(
494
+ stemX2,
495
+ stemY2,
496
+ direction,
497
+ stroke
498
+ )
496
499
 
497
500
  return (
498
501
  '<line x1="' +
@@ -500,6 +503,38 @@ export class SchematicPowerPortRenderer {
500
503
  '" y1="' +
501
504
  formatNumber(y) +
502
505
  '" x2="' +
506
+ formatNumber(stemX2) +
507
+ '" y2="' +
508
+ formatNumber(stemY2) +
509
+ '" stroke="' +
510
+ stroke +
511
+ '" />' +
512
+ capLine
513
+ )
514
+ }
515
+
516
+ /**
517
+ * Builds the perpendicular rail cap at the end of a power-port stem.
518
+ * @param {number} x Stem endpoint x coordinate.
519
+ * @param {number} y Stem endpoint y coordinate.
520
+ * @param {'up' | 'down' | 'left' | 'right'} direction Stem direction.
521
+ * @param {string} stroke Escaped SVG stroke color.
522
+ * @returns {string}
523
+ */
524
+ static #buildRailCapLine(x, y, direction, stroke) {
525
+ const halfLength = 6
526
+ const isVerticalStem = direction === 'up' || direction === 'down'
527
+ const x1 = isVerticalStem ? x - halfLength : x
528
+ const y1 = isVerticalStem ? y : y - halfLength
529
+ const x2 = isVerticalStem ? x + halfLength : x
530
+ const y2 = isVerticalStem ? y : y + halfLength
531
+
532
+ return (
533
+ '<line x1="' +
534
+ formatNumber(x1) +
535
+ '" y1="' +
536
+ formatNumber(y1) +
537
+ '" x2="' +
503
538
  formatNumber(x2) +
504
539
  '" y2="' +
505
540
  formatNumber(y2) +
@@ -516,10 +551,8 @@ export class SchematicPowerPortRenderer {
516
551
  * @param {number} x
517
552
  * @param {number} y
518
553
  * @param {number} fontSize
519
- * @param {{ fontSize?: number, fontFamily?: string, fontWeight?: number }} labelOptions
554
+ * @param {{ fontSize?: number, fontFamily?: string, fontWeight?: number, fontStyle?: string }} labelOptions
520
555
  * @param {string} color
521
- * @param {{ x1: number, y1: number, x2: number, y2: number }[]} lines
522
- * @param {number} sheetHeight
523
556
  * @returns {string}
524
557
  */
525
558
  static #buildDirectionalLabel(
@@ -529,18 +562,14 @@ export class SchematicPowerPortRenderer {
529
562
  y,
530
563
  fontSize,
531
564
  labelOptions,
532
- color,
533
- lines = [],
534
- sheetHeight = 0
565
+ color
535
566
  ) {
536
567
  const placement = SchematicPowerPortRenderer.#resolveLabelPlacement(
537
568
  text,
538
569
  direction,
539
570
  x,
540
571
  y,
541
- fontSize,
542
- lines,
543
- sheetHeight
572
+ fontSize
544
573
  )
545
574
 
546
575
  return createSvgText(
@@ -555,38 +584,19 @@ export class SchematicPowerPortRenderer {
555
584
  }
556
585
 
557
586
  /**
558
- * Resolves label placement and avoids nearby horizontal net-line overlap
559
- * for upward rail labels in dense schematic areas.
587
+ * Resolves label placement from the power-port symbol direction.
560
588
  * @param {{ text: string, style?: number }} text
561
589
  * @param {'up' | 'down' | 'left' | 'right'} direction
562
590
  * @param {number} x
563
591
  * @param {number} y
564
592
  * @param {number} fontSize
565
- * @param {{ x1: number, y1: number, x2: number, y2: number }[]} lines
566
- * @param {number} sheetHeight
567
593
  * @returns {{ x: number, y: number, anchor: 'start' | 'middle' | 'end' }}
568
594
  */
569
- static #resolveLabelPlacement(
570
- text,
571
- direction,
572
- x,
573
- y,
574
- fontSize,
575
- lines,
576
- sheetHeight
577
- ) {
595
+ static #resolveLabelPlacement(text, direction, x, y, fontSize) {
578
596
  if (direction === 'up') {
579
597
  return {
580
598
  x,
581
- y: SchematicPowerPortRenderer.#resolveUpwardRailLabelY(
582
- text,
583
- x,
584
- y,
585
- y - 16,
586
- fontSize,
587
- lines,
588
- sheetHeight
589
- ),
599
+ y: y - 14,
590
600
  anchor: 'middle'
591
601
  }
592
602
  }
@@ -601,121 +611,4 @@ export class SchematicPowerPortRenderer {
601
611
 
602
612
  return { x, y: y + 25, anchor: 'middle' }
603
613
  }
604
-
605
- /**
606
- * Shifts an upward rail label below close parallel net lines when the
607
- * default placement would draw text through an existing wire.
608
- * @param {{ text: string, style?: number }} text
609
- * @param {number} x
610
- * @param {number} connectionY
611
- * @param {number} defaultY
612
- * @param {number} fontSize
613
- * @param {{ x1: number, y1: number, x2: number, y2: number }[]} lines
614
- * @param {number} sheetHeight
615
- * @returns {number}
616
- */
617
- static #resolveUpwardRailLabelY(
618
- text,
619
- x,
620
- connectionY,
621
- defaultY,
622
- fontSize,
623
- lines,
624
- sheetHeight
625
- ) {
626
- if (Number(text.style || 0) === 4) {
627
- return defaultY
628
- }
629
-
630
- let labelY = defaultY
631
-
632
- for (const line of lines) {
633
- const horizontalLine =
634
- SchematicPowerPortRenderer.#projectHorizontalLine(
635
- line,
636
- sheetHeight
637
- )
638
-
639
- if (!horizontalLine) {
640
- continue
641
- }
642
-
643
- if (
644
- !SchematicPowerPortRenderer.#horizontalLineIntersectsLabel(
645
- horizontalLine,
646
- text.text,
647
- x,
648
- labelY,
649
- fontSize
650
- )
651
- ) {
652
- continue
653
- }
654
-
655
- labelY = Math.min(
656
- connectionY - 4,
657
- Math.max(labelY, horizontalLine.y + fontSize + 4)
658
- )
659
- }
660
-
661
- return labelY
662
- }
663
-
664
- /**
665
- * Projects one source horizontal line into SVG coordinates.
666
- * @param {{ x1: number, y1: number, x2: number, y2: number }} line
667
- * @param {number} sheetHeight
668
- * @returns {{ y: number, minX: number, maxX: number } | null}
669
- */
670
- static #projectHorizontalLine(line, sheetHeight) {
671
- const y1 = projectSchematicY(sheetHeight, line.y1)
672
- const y2 = projectSchematicY(sheetHeight, line.y2)
673
-
674
- if (Math.abs(y1 - y2) > 0.01) {
675
- return null
676
- }
677
-
678
- return {
679
- y: y1,
680
- minX: Math.min(line.x1, line.x2),
681
- maxX: Math.max(line.x1, line.x2)
682
- }
683
- }
684
-
685
- /**
686
- * Checks whether a projected horizontal line crosses an estimated text box.
687
- * @param {{ y: number, minX: number, maxX: number }} line
688
- * @param {string} label
689
- * @param {number} x
690
- * @param {number} labelY
691
- * @param {number} fontSize
692
- * @returns {boolean}
693
- */
694
- static #horizontalLineIntersectsLabel(line, label, x, labelY, fontSize) {
695
- const textWidth = SchematicPowerPortRenderer.#estimateLabelWidth(
696
- label,
697
- fontSize
698
- )
699
- const textMinX = x - textWidth / 2
700
- const textMaxX = x + textWidth / 2
701
- const textTopY = labelY - fontSize
702
- const textBottomY = labelY + fontSize * 0.25
703
-
704
- return (
705
- line.maxX >= textMinX &&
706
- line.minX <= textMaxX &&
707
- line.y >= textTopY &&
708
- line.y <= textBottomY
709
- )
710
- }
711
-
712
- /**
713
- * Estimates one power-port label width for clearance checks.
714
- * @param {string} label
715
- * @param {number} fontSize
716
- * @returns {number}
717
- */
718
- static #estimateLabelWidth(label, fontSize) {
719
- return String(label || '').length * fontSize * 0.56
720
- }
721
614
  }
@@ -515,6 +515,10 @@ export class SchematicSheetChromeRenderer {
515
515
  SchematicSheetChromeRenderer.#buildSheetValue(titleBlock)
516
516
  const sheetValueHint =
517
517
  SchematicSheetChromeRenderer.#buildSheetValueFooterHint(titleBlock)
518
+ const footerValueOptions =
519
+ SchematicSheetChromeRenderer.#resolveTitleBlockFooterValueOptions(
520
+ titleBlock
521
+ )
518
522
 
519
523
  return (
520
524
  '<g class="sheet-title-block">' +
@@ -702,7 +706,8 @@ export class SchematicSheetChromeRenderer {
702
706
  valueRowY,
703
707
  sheet?.paperSize || 'A4',
704
708
  'var(--schematic-text-color)',
705
- 'middle'
709
+ 'middle',
710
+ footerValueOptions
706
711
  ) +
707
712
  SchematicSheetChromeRenderer.#buildTitleBlockValueMarkup(
708
713
  x + titleBlockWidth * 0.415,
@@ -718,7 +723,8 @@ export class SchematicSheetChromeRenderer {
718
723
  footerDateY,
719
724
  renderedDate,
720
725
  'var(--schematic-text-color)',
721
- 'start'
726
+ 'start',
727
+ footerValueOptions
722
728
  ) +
723
729
  createSvgText(
724
730
  'sheet-title-value',
@@ -726,7 +732,8 @@ export class SchematicSheetChromeRenderer {
726
732
  footerFileY,
727
733
  renderedFileName,
728
734
  'var(--schematic-text-color)',
729
- 'start'
735
+ 'start',
736
+ footerValueOptions
730
737
  ) +
731
738
  createSvgText(
732
739
  'sheet-title-value',
@@ -734,7 +741,8 @@ export class SchematicSheetChromeRenderer {
734
741
  footerFileY,
735
742
  titleBlock.drawnBy || '',
736
743
  'var(--schematic-default-ink-color)',
737
- 'middle'
744
+ 'middle',
745
+ footerValueOptions
738
746
  ) +
739
747
  '</g>'
740
748
  )
@@ -759,8 +767,8 @@ export class SchematicSheetChromeRenderer {
759
767
  /**
760
768
  * Resolves default serif typography for synthesized footer values that do
761
769
  * not have their own recovered hint styling.
762
- * @param {{ footerHints?: Partial<Record<'title' | 'documentNumber' | 'revision', { fontFamily: string }>> }} titleBlock
763
- * @returns {{ fontSize: number, fontFamily: string, fontWeight: number }}
770
+ * @param {{ footerHints?: Partial<Record<'title' | 'documentNumber' | 'revision', { fontFamily: string, fontStyle?: string }>> }} titleBlock
771
+ * @returns {{ fontSize: number, fontFamily: string, fontWeight: number, fontStyle?: string }}
764
772
  */
765
773
  static #resolveTitleBlockFooterValueOptions(titleBlock) {
766
774
  return {
@@ -769,7 +777,10 @@ export class SchematicSheetChromeRenderer {
769
777
  titleBlock?.footerHints?.revision?.fontFamily ||
770
778
  titleBlock?.footerHints?.title?.fontFamily ||
771
779
  'Times New Roman',
772
- fontWeight: 400
780
+ fontWeight: 400,
781
+ fontStyle:
782
+ titleBlock?.footerHints?.revision?.fontStyle ||
783
+ titleBlock?.footerHints?.title?.fontStyle
773
784
  }
774
785
  }
775
786
 
@@ -779,7 +790,7 @@ export class SchematicSheetChromeRenderer {
779
790
  * @param {number} x
780
791
  * @param {number} y
781
792
  * @param {string} text
782
- * @param {{ fontSize: number, fontFamily: string, fontWeight: number }} options
793
+ * @param {{ fontSize: number, fontFamily: string, fontWeight: number, fontStyle?: string }} options
783
794
  * @returns {string}
784
795
  */
785
796
  static #buildTitleBlockLabelMarkup(x, y, text, options) {
@@ -800,7 +811,7 @@ export class SchematicSheetChromeRenderer {
800
811
  * @param {number} resolvedY
801
812
  * @param {string} text
802
813
  * @param {string} fallbackColor
803
- * @param {{ x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number } | undefined} footerHint
814
+ * @param {{ x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number, fontStyle?: string } | undefined} footerHint
804
815
  * @param {number} sheetHeight
805
816
  * @param {boolean} [preserveHintX=true]
806
817
  * @returns {string}
@@ -838,7 +849,8 @@ export class SchematicSheetChromeRenderer {
838
849
  {
839
850
  fontSize: footerHint.fontSize,
840
851
  fontFamily: footerHint.fontFamily,
841
- fontWeight: footerHint.fontWeight
852
+ fontWeight: footerHint.fontWeight,
853
+ fontStyle: footerHint.fontStyle
842
854
  }
843
855
  )
844
856
  }
@@ -850,7 +862,7 @@ export class SchematicSheetChromeRenderer {
850
862
  * @param {number} fallbackY
851
863
  * @param {string} text
852
864
  * @param {string} fallbackColor
853
- * @param {{ x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number } | undefined} footerHint
865
+ * @param {{ x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number, fontStyle?: string } | undefined} footerHint
854
866
  * @param {number} sheetHeight
855
867
  * @param {boolean} [preserveHintX=true]
856
868
  * @returns {string}
@@ -888,15 +900,16 @@ export class SchematicSheetChromeRenderer {
888
900
  {
889
901
  fontSize: footerHint.fontSize,
890
902
  fontFamily: footerHint.fontFamily,
891
- fontWeight: footerHint.fontWeight
903
+ fontWeight: footerHint.fontWeight,
904
+ fontStyle: footerHint.fontStyle
892
905
  }
893
906
  )
894
907
  }
895
908
 
896
909
  /**
897
910
  * Builds one combined sheet-value hint from the recovered sheet-number row.
898
- * @param {{ footerHints?: Partial<Record<'sheetNumber' | 'sheetTotal', { x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number }>> }} titleBlock
899
- * @returns {{ x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number } | undefined}
911
+ * @param {{ footerHints?: Partial<Record<'sheetNumber' | 'sheetTotal', { x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number, fontStyle?: string }>> }} titleBlock
912
+ * @returns {{ x: number, y: number, color: string, fontSize: number, fontFamily: string, fontWeight: number, fontStyle?: string } | undefined}
900
913
  */
901
914
  static #buildSheetValueFooterHint(titleBlock) {
902
915
  const sheetNumberHint = titleBlock?.footerHints?.sheetNumber
@@ -912,7 +925,8 @@ export class SchematicSheetChromeRenderer {
912
925
  color: sheetNumberHint.color,
913
926
  fontSize: sheetNumberHint.fontSize,
914
927
  fontFamily: sheetNumberHint.fontFamily,
915
- fontWeight: sheetNumberHint.fontWeight
928
+ fontWeight: sheetNumberHint.fontWeight,
929
+ fontStyle: sheetNumberHint.fontStyle
916
930
  }
917
931
  }
918
932