Skip to content

Commit 8acb5b7

Browse files
committed
Keep part labels readable over jumper wires
1 parent 06033ed commit 8acb5b7

4 files changed

Lines changed: 62 additions & 24 deletions

File tree

‎lib/breadkit/render/svg_renderer.rb‎

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ def render(circuit, crop: "auto", theme: "light", orientation: "portrait", show_
150150
svg << "<g id=\"components\">#{components_svg}</g>"
151151
svg << "<g id=\"offboard\">#{offboard_svg}</g>"
152152
svg << "<g id=\"wires\">#{wires_svg}</g>"
153-
svg << "<g id=\"component-labels\">#{component_labels_overlay_svg}</g>" if @multi_board
153+
labels = component_labels_overlay_svg
154+
svg << "<g id=\"component-labels\">#{labels}</g>" unless labels.empty?
154155
svg << "<g id=\"labels\">#{labels_svg}</g>"
155156
svg << "<g id=\"nets\">#{nets_svg}</g><g id=\"annotations\">#{annotations_svg}</g>"
156157
svg << "</g>" if transform
@@ -380,24 +381,32 @@ def resistor_svg(component, pins, x, y)
380381
out << rect(x - 12, y - 4, 24, 8, rx: 3, fill: @colors[:resistor_bg], stroke: @colors[:resistor_border], data_ref: component.ref)
381382
bands = resistor_bands(component.value, bands: component.attrs[:bands] || 4)
382383
bands.each_with_index { |color, index| out << rect(x - (bands.length == 5 ? 9 : 7) + index * 4, y - 4, 1.7, 8, fill: color) }
383-
out << text(x, y - 7, "#{component.ref} #{component.value}", "font-size" => 5, "font-weight" => 500, "text-anchor" => "middle") unless @multi_board
384384
out.join
385385
end
386386

387387
def component_labels_overlay_svg
388388
@visible_components.filter_map do |component|
389-
next unless component.part.data.dig("render", "shape") == "resistor"
390-
391389
holes = component.pins.values.filter_map { |pin| @circuit.board.hole(pin.hole_id) }
392390
next if holes.empty?
393391

394-
x = (holes.map { |hole| px(hole.x) }.minmax.sum) / 2.0
395-
y = (holes.map { |hole| py(hole.y) }.minmax.sum) / 2.0
396-
label = "#{component.ref} #{component.value}"
397-
width = [label.length * 3.1 + 6, 20].max
392+
xs, ys = holes.map { |hole| px(display_hole(hole).x) }, holes.map { |hole| py(display_hole(hole).y) }
393+
x, y = xs.minmax.sum / 2.0, ys.minmax.sum / 2.0
394+
shape = component.part.data.dig("render", "shape")
395+
label, baseline, font_size, background, foreground = case shape
396+
when "resistor" then ["#{component.ref} #{component.value}", y - 7, 5, @colors[:board], @colors[:text]]
397+
when "led_5mm" then [component.ref, y - 8, 5, @colors[:board], @colors[:text]]
398+
when "diode" then [component.ref, y - 5, 4.5, @colors[:board], @colors[:text]]
399+
when "capacitor", "electrolytic" then [component.ref, y - 6, 4.5, @colors[:board], @colors[:text]]
400+
when "dip" then [component.part.data.dig("render", "label") || component.ref, y + 1.5, 4.5, @colors[:dip_bg], @colors[:dip_text]]
401+
else next
402+
end
403+
width = [label.to_s.length * font_size * 0.62 + 6, 13].max
404+
background_options = { rx: 2, fill: background }
405+
background_options[:transform] = "rotate(-90 #{fmt(x)} #{fmt(baseline)})" if @rotated_scene
398406
opacity = @emphasis_active && !emphasized_component?(component.ref) ? " opacity=\"0.18\"" : ""
399-
body = rect(x - width / 2, y - 14, width, 9, rx: 2, fill: @colors[:board])
400-
body += text(x, y - 7, label, "font-size" => 5, "font-weight" => 500, "text-anchor" => "middle")
407+
body = rect(x - width / 2, baseline - font_size - 2, width, font_size + 4, **background_options)
408+
body += text(x, baseline, label, "font-size" => font_size, "font-weight" => 500,
409+
"fill" => foreground, "text-anchor" => "middle")
401410
layer_group("<g data-ref=\"#{escape(component.ref)}\"#{opacity}>#{body}</g>", read(component.attrs, "layer"))
402411
end.join
403412
end
@@ -410,14 +419,12 @@ def led_svg(component, pins, x, y)
410419
color = { "red" => "#df5550", "green" => "#5fbf69", "blue" => "#6a9df0", "yellow" => "#f0d958" }.fetch(color, color)
411420
out << circle(x, y, 5.5, fill: color, stroke: @colors[:component_border], data_ref: component.ref)
412421
out << polarity_marker(component, pins, x, y, 5.0, @colors[:component_border])
413-
out << text(x, y - 8, component.ref, "font-size" => 5, "font-weight" => 500, "text-anchor" => "middle")
414422
out.join
415423
end
416424

417425
def diode_svg(component, pins, x, y)
418426
[lead_lines(pins, x, y), rect(x - 7, y - 3, 14, 6, rx: 1, fill: @colors[:diode_bg], stroke: @colors[:diode_border], data_ref: component.ref),
419-
polarity_marker(component, pins, x, y, 3.0, @colors[:diode_mark]),
420-
text(x, y - 5, component.ref, "font-size" => 4.5, "font-weight" => 500, "text-anchor" => "middle")].join
427+
polarity_marker(component, pins, x, y, 3.0, @colors[:diode_mark])].join
421428
end
422429

423430
def capacitor_svg(component, pins, x, y, electrolytic:)
@@ -430,7 +437,6 @@ def capacitor_svg(component, pins, x, y, electrolytic:)
430437
offset_y = offset_x.zero? ? (positive && positive[1] > y ? 5 : -5) : 0
431438
out << text(x + offset_x, y + offset_y + 1.5, "+", "font-size" => 4, "text-anchor" => "middle")
432439
end
433-
out << text(x, y - 6, component.ref, "font-size" => 4.5, "font-weight" => 500, "text-anchor" => "middle")
434440
out.join
435441
end
436442

@@ -442,8 +448,6 @@ def dip_svg(component, pins)
442448
notch_x = first_pin && px(first_pin.x) > xs.sum / xs.length ? x2 - 2 : x1 + 2
443449
out << tag("path", d: "M #{fmt(notch_x)} #{fmt(middle - 2)} Q #{fmt(notch_x + (notch_x == x1 + 2 ? 2 : -2))} #{fmt(middle)} #{fmt(notch_x)} #{fmt(middle + 2)}",
444450
fill: "none", stroke: @colors[:dip_notch], stroke_width: 0.8)
445-
out << text((x1 + x2) / 2, middle + 1.5, component.part.data.dig("render", "label") || component.ref,
446-
"font-size" => 4.5, "fill" => @colors[:dip_text], "text-anchor" => "middle")
447451
pins.each do |pin, hole|
448452
display = display_hole(hole)
449453
out << text(px(display.x), py(display.y) + (display.y > 4 ? 4 : -2), pin.number, "font-size" => 3.5, "text-anchor" => "middle")

‎spec/breadkit/render_spec.rb‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@
1414
expect(described_class.new.render(circuit, legend: true, show_nets: true)).to eq(svg)
1515
end
1616

17+
it "keeps component labels above jumper wires in dense circuits" do
18+
%w[02_555_blinker 04_led_bar].each do |name|
19+
path = File.expand_path("../../../breadkit/examples/#{name}.bk.rb", __dir__)
20+
svg = described_class.new.render(Breadkit.load(path))
21+
document = REXML::Document.new(svg)
22+
expect(svg.index('id="wires"')).to be < svg.index('id="component-labels"')
23+
label = REXML::XPath.first(document, "//g[@id='component-labels']/g[@data-ref='R1']")
24+
expect(label.elements["rect"].attributes["transform"]).to eq(label.elements["text"].attributes["transform"])
25+
expect(REXML::XPath.first(document, "//g[@id='component-labels']/g[@data-ref='D1']/text").text).to eq("D1")
26+
end
27+
path = File.expand_path("../../../breadkit/examples/02_555_blinker.bk.rb", __dir__)
28+
document = REXML::Document.new(described_class.new.render(Breadkit.load(path)))
29+
expect(REXML::XPath.first(document, "//g[@id='component-labels']/g[@data-ref='U1']/text").text).to eq("NE555")
30+
end
31+
1732
it "draws model-specific seven-segment and RGB LED bodies" do
1833
builder = Breadkit::DSL::Builder.new
1934
builder.instance_eval('board :full; part :SEG1, :sc56_11ewa, at: "b1"; part :RGB1, :wp154a4sureqbfzgc, pins: %w[a15 a16 a17 a18]', "displays.bk.rb", 1)
@@ -509,6 +524,23 @@
509524
end
510525

511526
RSpec.describe Breadkit::Render::CLI do
527+
it "prints warnings in every view without --force" do
528+
builder = Breadkit::DSL::Builder.new
529+
builder.instance_eval('board :mini; resistor :R1, "330", pins: %w[a1 a3]', "warning.bk.rb", 1)
530+
diagram = Breadkit::Resolver.new.call(builder.document)
531+
diagram.diagnostics << Breadkit::Diagnostic.new(code: "warning", severity: "warning", message: "check placement", location: nil, targets: [])
532+
allow(Breadkit).to receive(:load).and_return(diagram)
533+
534+
Dir.mktmpdir do |directory|
535+
%w[breadboard netlist schematic].each do |view|
536+
output = File.join(directory, "#{view}.svg")
537+
expect { expect(described_class.new.run(["warning.bk.rb", "--view", view, "-o", output])).to eq(0) }
538+
.to output(/warning: check placement/).to_stderr
539+
expect(File).to exist(output)
540+
end
541+
end
542+
end
543+
512544
it "prints warnings and forced errors to stderr" do
513545
input = File.expand_path("../../../breadkit/examples/01_led_button.bk.rb", __dir__)
514546
diagram = Breadkit.load(input)

0 commit comments

Comments
 (0)