diff --git a/diylc/diylc-core/src/org/diylc/netlist/Tree.java b/diylc/diylc-core/src/org/diylc/netlist/Tree.java index 42520387..f9ee373a 100644 --- a/diylc/diylc-core/src/org/diylc/netlist/Tree.java +++ b/diylc/diylc-core/src/org/diylc/netlist/Tree.java @@ -22,11 +22,14 @@ package org.diylc.netlist; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; + import org.diylc.core.IDIYComponent; public class Tree { @@ -157,6 +160,27 @@ public class Tree { return sb.toString(); } + public String toAsciiString() { + return String.join("
", toAscii()); + } + + private List toAscii() { + if (leaf != null) { + return Arrays.asList(TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1) + " " + leaf.toHTML() + " " + TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1)); + } + + List children = getOrderedChildren(); + + List> childrenAscii = + children.stream().map(c -> c.toAscii()).collect(Collectors.toList()); + + if (TreeConnectionType.Series.equals(connectionType)) { + return TreeAsciiUtil.concatenateMultiLineSerial(TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1), childrenAscii); + } else { + return TreeAsciiUtil.concatenateMultiLineParallel(childrenAscii); + } + } + @Override protected Object clone() throws CloneNotSupportedException { if (leaf != null) diff --git a/diylc/diylc-core/src/org/diylc/netlist/TreeAsciiUtil.java b/diylc/diylc-core/src/org/diylc/netlist/TreeAsciiUtil.java new file mode 100644 index 00000000..878a3b1a --- /dev/null +++ b/diylc/diylc-core/src/org/diylc/netlist/TreeAsciiUtil.java @@ -0,0 +1,209 @@ +package org.diylc.netlist; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class TreeAsciiUtil { + + public static final String BOXVR = "├"; + public static final String BOXUL = "┘"; + public static final String BOXDL = "┐"; + public static final String BOXH = "─"; + public static final String NBSP = " "; + public static final String BOXDR = "┌"; + public static final String BOXUR = "└"; + public static final String BOXVH = "┼"; + public static final String BOXVL = "┤"; + public static final String BOXV = "│"; + + public static List concatenateMultiLineParallel(List> elementLines) { + + List lineCounts = elementLines.stream() + .map(lines -> lines.size()) + .collect(Collectors.toList()); + List lineLengths = elementLines.stream() + .map(element -> element.stream() + .map(TreeAsciiUtil::htmlLength) + .max(Integer::compare) + .orElse(0)) + .collect(Collectors.toList()); + int maxLineLength = Collections.max(lineLengths); + int targetLineCount = lineCounts.stream() + .mapToInt(Integer::intValue) + .sum() + elementLines.size() - 1; + int centerLine = targetLineCount / 2; + + List results = new ArrayList(); + int overallLineCount = 0; + for (int i = 0; i < elementLines.size(); i++) { + List lines = elementLines.get(i); + + int lineLength = lineLengths.get(i); + + String leftBuffer = ""; + String rightBuffer = ""; + if (lineLength < maxLineLength) { + leftBuffer = generateString(" ", (maxLineLength - lineLength) / 2); + rightBuffer = generateString(" ", (maxLineLength - lineLength) - (maxLineLength - lineLength) / 2); + } + + String leftBufferCentral = ""; + String rightBufferCentral = ""; + if (lineLength < maxLineLength) { + leftBufferCentral = generateString(BOXH, (maxLineLength - lineLength) / 2); + rightBufferCentral = generateString(BOXH, (maxLineLength - lineLength) - (maxLineLength - lineLength) / 2); + } + + int elementCenterLine = lines.size() / 2; + + for (int j = 0; j < lines.size(); j++) { + String line = lines.get(j); + + StringBuilder sb = new StringBuilder(); + + if (overallLineCount == centerLine) { + sb.append(BOXH); + } else { + sb.append(NBSP); + } + + if ((j < elementCenterLine && i == 0) || (j > elementCenterLine && i == elementLines.size() - 1)) { + sb.append(NBSP); + } else if (j == elementCenterLine && i == 0) { + sb.append(BOXDR); + } else if (i == elementLines.size() - 1 && j == elementCenterLine) { + sb.append(BOXUR); + } else if (overallLineCount == centerLine) { + if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) { + sb.append(BOXVH); + } else { + sb.append(BOXVL); + } + } else { + if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) { + sb.append(BOXVR); + } else { + sb.append(BOXV); + } + } + + if (j == elementCenterLine) { + sb.append(leftBufferCentral); + } else { + sb.append(leftBuffer); + } + sb.append(line); + if (j == elementCenterLine) { + sb.append(rightBufferCentral); + } else { + sb.append(rightBuffer); + } + + if ((j < elementCenterLine && i == 0) || (j > elementCenterLine && i == elementLines.size() - 1)) { + sb.append(NBSP); + } else if (j == elementCenterLine && i == 0) { + sb.append(BOXDL); + } else if (i == elementLines.size() - 1 && j == elementCenterLine) { + sb.append(BOXUL); + } else if (overallLineCount == centerLine) { + if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) { + sb.append(BOXVH); + } else { + sb.append(BOXVR); + } + } else { + if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) { + sb.append(BOXVL); + } else { + sb.append(BOXV); + } + } + + if (overallLineCount == centerLine) { + sb.append(BOXH); + } else { + sb.append(NBSP); + } + + results.add(sb.toString()); + overallLineCount++; + } + + + if (i < elementLines.size() - 1) { + String leftEnding; + String rightEnding; + if (overallLineCount == centerLine) { + leftEnding = BOXH + BOXVL; + rightEnding = BOXVR + BOXH; + } else { + leftEnding = NBSP + BOXV; + rightEnding = BOXV + NBSP; + } + String emptyLine = leftEnding + generateString(NBSP, maxLineLength) + rightEnding; + results.add(emptyLine); + overallLineCount++; + } + } + + return results; + } + + public static List concatenateMultiLineSerial(String separator, List> elementLines) { + List lineCounts = elementLines.stream() + .map(lines -> lines.size()) + .collect(Collectors.toList()); + String blankSeparator = generateString(NBSP, htmlLength(separator)); + int maxLineCount = lineCounts.stream().mapToInt(Integer::intValue).max().orElse(0); + int centerLine = maxLineCount / 2; + List lineLengths = elementLines.stream() + .map(element -> element.stream() + .map(TreeAsciiUtil::htmlLength) + .max(Integer::compare) + .orElse(0)) + .collect(Collectors.toList()); + List results = new ArrayList<>(); + for (int line = 0; line < maxLineCount; line++) { + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < elementLines.size(); i++) { + int elementLineCount = lineCounts.get(i); + int elementCenterLine = elementLineCount / 2; + int elementLineLength = lineLengths.get(i); + if (line < centerLine - elementCenterLine + || line > centerLine - elementCenterLine + elementLineCount) { + sb.append(generateString(NBSP, elementLineLength)); + } else { + int lineIndex = line + elementCenterLine - centerLine; + List element = elementLines.get(i); + String elementLine; + if (lineIndex < element.size()) { + elementLine = element.get(lineIndex); + } else { + elementLine = generateString(NBSP, elementLineLength); + } + sb.append(elementLine); + } + if (i < elementLines.size() - 1) { + if (line == centerLine) { + sb.append(separator); + } else { + sb.append(blankSeparator); + } + } + } + results.add(sb.toString()); + } + return results; + } + + public static String generateString(String seed, int n) { + return String.join("", Collections.nCopies(n, seed)); + } + + public static int htmlLength(String s) { + return s.replaceAll("\\&.*?\\;", "x").length(); + } +} diff --git a/diylc/diylc-core/src/org/diylc/netlist/TreeLeaf.java b/diylc/diylc-core/src/org/diylc/netlist/TreeLeaf.java index 737fcff7..11cfcd37 100644 --- a/diylc/diylc-core/src/org/diylc/netlist/TreeLeaf.java +++ b/diylc/diylc-core/src/org/diylc/netlist/TreeLeaf.java @@ -107,6 +107,6 @@ public class TreeLeaf implements ITree { } public String toHTML() { - return toString().replace("<", "<").replace(">", ">"); + return toString().replace("<", "<").replace(">", ">"); } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml index d97f2743..0510c9d5 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/update.xml +++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml @@ -4425,5 +4425,25 @@ https://github.com/bancika/diy-layout-creator/releases + + + 4 + 40 + 0 + + 2025-01-25 00:00:00.000 CET + + + + NEW_FEATURE + Guitar wiring analyzer can now detect treble bleed circuit on volume controls + + + IMPROVEMENT + Better rendering of the effective circuit graph in the guitar wiring analyzer, showing series and parallel connections in a more intuitive way + + + https://github.com/bancika/diy-layout-creator/releases + diff --git a/diylc/diylc-core/test/org/diylc/netlist/TreeAsciiUtilTest.java b/diylc/diylc-core/test/org/diylc/netlist/TreeAsciiUtilTest.java new file mode 100644 index 00000000..ee1df9e1 --- /dev/null +++ b/diylc/diylc-core/test/org/diylc/netlist/TreeAsciiUtilTest.java @@ -0,0 +1,57 @@ +package org.diylc.netlist; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class TreeAsciiUtilTest { + + @Test + public void testConcatenateMultiLineSerial() { + List result = TreeAsciiUtil.concatenateMultiLineSerial("-", + Arrays.asList(Arrays.asList("xx", "yy"), Arrays.asList("xxx", "yyy", "aaa"))); + assertNotNull(result); + assertEquals(3, result.size()); + assertEquals("xx xxx", result.get(0)); + assertEquals("yy-yyy", result.get(1)); + assertEquals("   aaa", result.get(2)); + } + + @Test + public void testConcatenateMultiLineSerial_singleInput() { + List> elementList = Arrays.asList(Arrays.asList("xx", "yy")); + List result = TreeAsciiUtil.concatenateMultiLineSerial("-", elementList); + assertNotNull(result); + assertEquals(Arrays.asList("xx", "yy"), result); + } + + @Test + public void testConcatenateMultiLineParallel() { + List result = TreeAsciiUtil.concatenateMultiLineParallel( + Arrays.asList(Arrays.asList("xx", "yy"), Arrays.asList("xxx", "yyy", "aaa"))); + + System.out.println("" + String.join("
", result) +"
"); + assertNotNull(result); +// assertEquals(3, result.size()); +// assertEquals("xx xxx", result.get(0)); +// assertEquals("yy-yyy", result.get(1)); +// assertEquals("   aaa", result.get(2)); + } + + @Test + public void testConcatenateMultiLineParallel_4_lines() { + List result = TreeAsciiUtil.concatenateMultiLineParallel( + Arrays.asList(Arrays.asList("xx"), Arrays.asList("xx"), Arrays.asList("xx"), Arrays.asList("xx"))); + + System.out.println("" + String.join("
", result) +"
"); + assertNotNull(result); +// assertEquals(3, result.size()); +// assertEquals("xx xxx", result.get(0)); +// assertEquals("yy-yyy", result.get(1)); +// assertEquals("   aaa", result.get(2)); + } +} diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar index e2d1f91c..ed04129e 100644 Binary files a/diylc/diylc-library/lib/diylc-core.jar and b/diylc/diylc-library/lib/diylc-core.jar differ diff --git a/diylc/diylc-library/src/org/diylc/netlist/GuitarDiagramAnalyzer.java b/diylc/diylc-library/src/org/diylc/netlist/GuitarDiagramAnalyzer.java index dc225c06..5827f814 100644 --- a/diylc/diylc-library/src/org/diylc/netlist/GuitarDiagramAnalyzer.java +++ b/diylc/diylc-library/src/org/diylc/netlist/GuitarDiagramAnalyzer.java @@ -20,6 +20,7 @@ package org.diylc.netlist; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -42,6 +43,7 @@ import org.diylc.components.passive.PotentiometerPanel; import org.diylc.components.passive.RadialCeramicDiskCapacitor; import org.diylc.components.passive.RadialFilmCapacitor; import org.diylc.components.passive.RadialMicaCapacitor; +import org.diylc.components.passive.Resistor; import org.diylc.core.IDIYComponent; import org.diylc.netlist.Tree.ITreeWalker; @@ -51,6 +53,9 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn private static Set POT_TYPES = new HashSet(); private static Set PICKUP_TYPES = new HashSet(); private static Set CAP_TYPES = new HashSet(); + private static Set RESISTOR_TYPES = new HashSet(); + private static Set RC_TYPES = new HashSet(); + static { JACK_TYPES.add(OpenJack1_4.class.getCanonicalName()); JACK_TYPES.add(ClosedJack1_4.class.getCanonicalName()); @@ -68,6 +73,11 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn CAP_TYPES.add(RadialMicaCapacitor.class.getCanonicalName()); CAP_TYPES.add(RadialFilmCapacitor.class.getCanonicalName()); CAP_TYPES.add(AxialFilmCapacitor.class.getCanonicalName()); + + RESISTOR_TYPES.add(Resistor.class.getCanonicalName()); + + RC_TYPES.addAll(CAP_TYPES); + RC_TYPES.addAll(RESISTOR_TYPES); } @Override @@ -203,6 +213,8 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn // locate volume and tone pots Map, List>> volPotPickupMap = new HashMap, List>>(); + Map, Set>> volPotTrebleBleedMap = + new HashMap, Set>>(); Map, List>> tonePotPickupMap = new HashMap, List>>(); Map, List>> tonePotPickupReverseMap = @@ -253,20 +265,22 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn if (tree1 == null || tree2 == null) continue; for (Map.Entry, Tree> r : pickupRoots.entrySet()) { - Tree parent1 = tree.findCommonParent(tree1, r.getValue()); - Tree parent2 = tree.findCommonParent(tree2, r.getValue()); - if (parent1 != null && parent2 != null - && parent1.getConnectionType() == TreeConnectionType.Series - && parent2.getConnectionType() == TreeConnectionType.Parallel) { + Tree commonParent1 = tree.findCommonParent(tree1, r.getValue()); + Tree commonParent2 = tree.findCommonParent(tree2, r.getValue()); + locateTrebleBleed(tree, volPotTrebleBleedMap, pot, tree1); + + if (commonParent1 != null && commonParent2 != null + && commonParent1.getConnectionType() == TreeConnectionType.Series + && commonParent2.getConnectionType() == TreeConnectionType.Parallel) { List> pickupList = volPotPickupMap.get(pot); if (pickupList == null) { pickupList = new ArrayList>(); volPotPickupMap.put(pot, pickupList); } pickupList.add(r.getKey()); - } else if (parent1 != null && parent2 != null - && parent1.getConnectionType() == TreeConnectionType.Parallel - && parent2.getConnectionType() == TreeConnectionType.Series) { + } else if (commonParent1 != null && commonParent2 != null + && commonParent1.getConnectionType() == TreeConnectionType.Parallel + && commonParent2.getConnectionType() == TreeConnectionType.Series) { List> pickupList = volPotPickupReverseMap.get(pot); if (pickupList == null) { pickupList = new ArrayList>(); @@ -290,6 +304,16 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn sb.append("'").append(pickupName).append("'"); } notes.add(sb.toString()); + + Set> trebleBleedComponents = volPotTrebleBleedMap.get(e.getKey()); + if (trebleBleedComponents != null && trebleBleedComponents.size() > 0) { + + String trebleBleadNote = trebleBleedComponents.stream() + .sorted(Comparator.comparing(c -> c.getName())) + .map(c -> "'" + c.getName() + "'") + .collect(Collectors.joining(", ")) + " form a treble bleed network on the '" + e.getKey().getName() + "' volume control"; + notes.add(trebleBleadNote); + } } for (Map.Entry, List>> e : volPotPickupReverseMap @@ -343,19 +367,39 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn } StringBuilder sb = new StringBuilder(); + + String asciiTree = tree.toAsciiString(); - sb.append("Parallel/Series connectivity tree:

").append(tree.toHTML(0)); + sb.append("

") + .append("") + .append(asciiTree) + .append(""); if (!notes.isEmpty()) { Collections.sort(notes); - sb.append("

Notes:
"); + sb.append("

"); for (String v : notes) { - sb.append("  ").append(v).append("
"); + sb.append(v).append("
"); } } return new Summary(netlist, sb.toString()); } + private void locateTrebleBleed(Tree tree, + Map, Set>> volPotTrebleBleedMap, IDIYComponent pot, + Tree tree1) { + Tree parent1 = tree1; + do { + parent1 = tree.findParent(parent1); + } while (parent1 != null && parent1.getChildren().size() <= 1); + if (parent1 != null) { + Set> trebleBleedComponents = parent1.extractComponents(RC_TYPES); + if (trebleBleedComponents.stream().anyMatch(c -> CAP_TYPES.contains(c.getClass().getCanonicalName()))) { + volPotTrebleBleedMap.computeIfAbsent(pot, k -> new HashSet>()).addAll(trebleBleedComponents); + } + } + } + public Tree constructTree(Netlist netlist) throws TreeException { List jackTipNodes = find(JACK_TYPES, "Tip", netlist); List jackSleeveNodes = find(JACK_TYPES, "Sleeve", netlist); diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar index e2d1f91c..ed04129e 100644 Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar index 590473f8..d0b20f5b 100644 Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ