mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-11 01:32:08 +10:00
Feature/paralel serial (#871)
* Parallel/serial * chore: fix parallel * chore: tree * graph improvements * fix html render * treble bleed detection
This commit is contained in:
@@ -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("<br>", toAscii());
|
||||
}
|
||||
|
||||
private List<String> toAscii() {
|
||||
if (leaf != null) {
|
||||
return Arrays.asList(TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1) + " " + leaf.toHTML() + " " + TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1));
|
||||
}
|
||||
|
||||
List<Tree> children = getOrderedChildren();
|
||||
|
||||
List<List<String>> 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)
|
||||
|
||||
@@ -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<String> concatenateMultiLineParallel(List<List<String>> elementLines) {
|
||||
|
||||
List<Integer> lineCounts = elementLines.stream()
|
||||
.map(lines -> lines.size())
|
||||
.collect(Collectors.toList());
|
||||
List<Integer> 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<String> results = new ArrayList<String>();
|
||||
int overallLineCount = 0;
|
||||
for (int i = 0; i < elementLines.size(); i++) {
|
||||
List<String> 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<String> concatenateMultiLineSerial(String separator, List<List<String>> elementLines) {
|
||||
List<Integer> 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<Integer> lineLengths = elementLines.stream()
|
||||
.map(element -> element.stream()
|
||||
.map(TreeAsciiUtil::htmlLength)
|
||||
.max(Integer::compare)
|
||||
.orElse(0))
|
||||
.collect(Collectors.toList());
|
||||
List<String> 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<String> 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();
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,6 @@ public class TreeLeaf implements ITree {
|
||||
}
|
||||
|
||||
public String toHTML() {
|
||||
return toString().replace("<", "<").replace(">", ">");
|
||||
return toString().replace("<", "<").replace(">", ">");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4425,5 +4425,25 @@
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>4</major>
|
||||
<minor>40</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-01-25 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Guitar wiring analyzer can now detect treble bleed circuit on volume controls</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Better rendering of the effective circuit graph in the guitar wiring analyzer, showing series and parallel connections in a more intuitive way</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
</a>
|
||||
</java.util.Arrays_-ArrayList>
|
||||
|
||||
@@ -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<String> 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<List<String>> elementList = Arrays.asList(Arrays.asList("xx", "yy"));
|
||||
List<String> result = TreeAsciiUtil.concatenateMultiLineSerial("-", elementList);
|
||||
assertNotNull(result);
|
||||
assertEquals(Arrays.asList("xx", "yy"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatenateMultiLineParallel() {
|
||||
List<String> result = TreeAsciiUtil.concatenateMultiLineParallel(
|
||||
Arrays.asList(Arrays.asList("xx", "yy"), Arrays.asList("xxx", "yyy", "aaa")));
|
||||
|
||||
System.out.println("<font face=\"Courier New\">" + String.join("<br>", result) +"</font>");
|
||||
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<String> result = TreeAsciiUtil.concatenateMultiLineParallel(
|
||||
Arrays.asList(Arrays.asList("xx"), Arrays.asList("xx"), Arrays.asList("xx"), Arrays.asList("xx")));
|
||||
|
||||
System.out.println("<font face=\"Courier New\">" + String.join("<br>", result) +"</font>");
|
||||
assertNotNull(result);
|
||||
// assertEquals(3, result.size());
|
||||
// assertEquals("xx xxx", result.get(0));
|
||||
// assertEquals("yy-yyy", result.get(1));
|
||||
// assertEquals(" aaa", result.get(2));
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -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<String> POT_TYPES = new HashSet<String>();
|
||||
private static Set<String> PICKUP_TYPES = new HashSet<String>();
|
||||
private static Set<String> CAP_TYPES = new HashSet<String>();
|
||||
private static Set<String> RESISTOR_TYPES = new HashSet<String>();
|
||||
private static Set<String> RC_TYPES = new HashSet<String>();
|
||||
|
||||
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<IDIYComponent<?>, List<IDIYComponent<?>>> volPotPickupMap =
|
||||
new HashMap<IDIYComponent<?>, List<IDIYComponent<?>>>();
|
||||
Map<IDIYComponent<?>, Set<IDIYComponent<?>>> volPotTrebleBleedMap =
|
||||
new HashMap<IDIYComponent<?>, Set<IDIYComponent<?>>>();
|
||||
Map<IDIYComponent<?>, List<IDIYComponent<?>>> tonePotPickupMap =
|
||||
new HashMap<IDIYComponent<?>, List<IDIYComponent<?>>>();
|
||||
Map<IDIYComponent<?>, List<IDIYComponent<?>>> tonePotPickupReverseMap =
|
||||
@@ -253,20 +265,22 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn
|
||||
if (tree1 == null || tree2 == null)
|
||||
continue;
|
||||
for (Map.Entry<IDIYComponent<?>, 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<IDIYComponent<?>> pickupList = volPotPickupMap.get(pot);
|
||||
if (pickupList == null) {
|
||||
pickupList = new ArrayList<IDIYComponent<?>>();
|
||||
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<IDIYComponent<?>> pickupList = volPotPickupReverseMap.get(pot);
|
||||
if (pickupList == null) {
|
||||
pickupList = new ArrayList<IDIYComponent<?>>();
|
||||
@@ -290,6 +304,16 @@ public class GuitarDiagramAnalyzer extends NetlistAnalyzer implements INetlistAn
|
||||
sb.append("'").append(pickupName).append("'");
|
||||
}
|
||||
notes.add(sb.toString());
|
||||
|
||||
Set<IDIYComponent<?>> 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<IDIYComponent<?>, List<IDIYComponent<?>>> 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:<br><br>").append(tree.toHTML(0));
|
||||
sb.append("<br><br>")
|
||||
.append("<font face='Courier New'>")
|
||||
.append(asciiTree)
|
||||
.append("</font>");
|
||||
if (!notes.isEmpty()) {
|
||||
Collections.sort(notes);
|
||||
sb.append("<br><br>Notes:<br>");
|
||||
sb.append("<br><br>");
|
||||
for (String v : notes) {
|
||||
sb.append(" ").append(v).append("<br>");
|
||||
sb.append(v).append("<br>");
|
||||
}
|
||||
}
|
||||
|
||||
return new Summary(netlist, sb.toString());
|
||||
}
|
||||
|
||||
private void locateTrebleBleed(Tree tree,
|
||||
Map<IDIYComponent<?>, Set<IDIYComponent<?>>> volPotTrebleBleedMap, IDIYComponent<?> pot,
|
||||
Tree tree1) {
|
||||
Tree parent1 = tree1;
|
||||
do {
|
||||
parent1 = tree.findParent(parent1);
|
||||
} while (parent1 != null && parent1.getChildren().size() <= 1);
|
||||
if (parent1 != null) {
|
||||
Set<IDIYComponent<?>> trebleBleedComponents = parent1.extractComponents(RC_TYPES);
|
||||
if (trebleBleedComponents.stream().anyMatch(c -> CAP_TYPES.contains(c.getClass().getCanonicalName()))) {
|
||||
volPotTrebleBleedMap.computeIfAbsent(pot, k -> new HashSet<IDIYComponent<?>>()).addAll(trebleBleedComponents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Tree constructTree(Netlist netlist) throws TreeException {
|
||||
List<Node> jackTipNodes = find(JACK_TYPES, "Tip", netlist);
|
||||
List<Node> jackSleeveNodes = find(JACK_TYPES, "Sleeve", netlist);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user