mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-08 12:54:55 +10:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c7d3ec45c | |||
| def979adf8 | |||
| 62ae5011ff | |||
| 8a1b3c271e | |||
| bd172ec0fd | |||
| b8a2e3a727 | |||
| c067118d77 | |||
| 9913e7ddb1 | |||
| ace11a8d7d | |||
| f152167602 | |||
| 3838b81612 | |||
| e5b7c4ff88 | |||
| 5f3e290bd0 | |||
| 9a9c875e8b | |||
| 9f8fd634cc | |||
| 8d4f6fd46c | |||
| b469ab9aa3 | |||
| 1492831678 | |||
| b997c18558 |
@@ -2,3 +2,4 @@
|
||||
*.iml
|
||||
*.eml
|
||||
/.project
|
||||
**/.DS_Store
|
||||
|
||||
@@ -35,19 +35,9 @@ public enum Orientation {
|
||||
}
|
||||
|
||||
public double toDegrees() {
|
||||
switch (this) {
|
||||
case DEFAULT:
|
||||
return 0;
|
||||
case _180:
|
||||
return 180;
|
||||
case _270:
|
||||
return 270;
|
||||
case _90:
|
||||
return 90;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
if (this == DEFAULT)
|
||||
return 0;
|
||||
return Integer.parseInt(name().replace("_", ""));
|
||||
}
|
||||
|
||||
public double toRadians() {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
DIY Layout Creator (DIYLC).
|
||||
Copyright (c) 2009-2018 held jointly by the individual authors.
|
||||
|
||||
This file is part of DIYLC.
|
||||
|
||||
DIYLC is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
DIYLC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
package org.diylc.common;
|
||||
|
||||
public enum Orientation45 {
|
||||
|
||||
DEFAULT, _45, _90, _135, _180, _225, _270, _315;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == DEFAULT) {
|
||||
return "Default";
|
||||
} else {
|
||||
return name().replace("_", "") + " degrees clockwise";
|
||||
}
|
||||
}
|
||||
|
||||
public double toDegrees() {
|
||||
if (this == DEFAULT)
|
||||
return 0;
|
||||
return Integer.parseInt(name().replace("_", ""));
|
||||
}
|
||||
|
||||
public double toRadians() {
|
||||
return Math.toRadians(toDegrees());
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.diylc.core;
|
||||
|
||||
public enum BoardUndersideDisplay {
|
||||
NONE, ABOVE, BELOW, LEFT, RIGHT;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString().charAt(0) + super.toString().substring(1).toLowerCase();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.diylc.core;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public interface IBoard {
|
||||
|
||||
Rectangle2D getBoardRectangle();
|
||||
|
||||
BoardUndersideDisplay getBoardUndersideDisplay();
|
||||
|
||||
boolean shouldExportToGerber();
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IBoard;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IView;
|
||||
import org.diylc.core.Project;
|
||||
@@ -30,15 +29,12 @@ public class GerberExporter {
|
||||
|
||||
public static void exportGerber(String fileNameBase, Project currentProject, IView view,
|
||||
Graphics2D graphics2d, String diylcVersion) {
|
||||
|
||||
List<IBoard> gerberBoards =
|
||||
currentProject.getComponents().stream()
|
||||
.filter(IBoard.class::isInstance)
|
||||
.map(IBoard.class::cast)
|
||||
.filter(IBoard::shouldExportToGerber).collect(Collectors.toList());
|
||||
List<IGerberBoard> gerberBoards =
|
||||
currentProject.getComponents().stream().filter(c -> c instanceof IGerberBoard)
|
||||
.map(x -> (IGerberBoard) x).collect(Collectors.toList());
|
||||
|
||||
Map<IBoard, List<IDIYComponent<?>>> boardComponentMap =
|
||||
new HashMap<IBoard, List<IDIYComponent<?>>>();
|
||||
Map<IGerberBoard, List<IDIYComponent<?>>> boardComponentMap =
|
||||
new HashMap<IGerberBoard, List<IDIYComponent<?>>>();
|
||||
|
||||
// DefaultGerberBoard defaultGerberBoard = new DefaultGerberBoard();
|
||||
|
||||
@@ -47,7 +43,7 @@ public class GerberExporter {
|
||||
currentProject.getComponents().stream().filter(c -> c instanceof IGerberComponent)
|
||||
.forEach(c -> {
|
||||
Point2D firstPoint = c.getControlPoint(0);
|
||||
IBoard board = gerberBoards.stream()
|
||||
IGerberBoard board = gerberBoards.stream()
|
||||
.filter(b -> b.getBoardRectangle().contains(firstPoint)).findFirst().orElse(null);
|
||||
if (board == null) {
|
||||
componentsWithoutBoard.add(c);
|
||||
|
||||
@@ -174,6 +174,7 @@ public class GerberG2DWrapper extends Graphics2D
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
GlyphVector gv = font.createGlyphVector(graphics2d.getFontRenderContext(), str);
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.diylc.core.gerber;
|
||||
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
public interface IGerberBoard {
|
||||
|
||||
Rectangle2D getBoardRectangle();
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.diylc.core.IDIYComponent;
|
||||
|
||||
public class Netlist implements Comparable<Netlist> {
|
||||
@@ -58,10 +60,6 @@ public class Netlist implements Comparable<Netlist> {
|
||||
return switchSetup;
|
||||
}
|
||||
|
||||
public void done() {
|
||||
Collections.sort(switchSetup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
@@ -86,11 +84,16 @@ public class Netlist implements Comparable<Netlist> {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getSwitchSetupString() {
|
||||
return getSwitchSetup().isEmpty() ? "[ ALL ]" : "[ " +
|
||||
getSwitchSetup().stream().map(x -> x.toString()).collect(Collectors.joining(",")) + " ]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Switch setup: ").append(getSwitchSetup()).append("\n\n");
|
||||
sb.append("Switch setup: ").append(getSwitchSetupString()).append("\n\n");
|
||||
List<Group> list = getSortedGroups();
|
||||
for (Group g : list) {
|
||||
sb.append("\t").append(g).append("\n");
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.awt.geom.Point2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -29,6 +30,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.core.ICommonNode;
|
||||
@@ -42,6 +44,7 @@ import org.diylc.presenter.ComponentProcessor;
|
||||
import org.diylc.presenter.Connection;
|
||||
import org.diylc.presenter.ContinuityArea;
|
||||
import org.diylc.utils.RTree;
|
||||
|
||||
import com.google.common.graph.GraphBuilder;
|
||||
import com.google.common.graph.ImmutableGraph;
|
||||
import com.google.common.graph.ImmutableGraph.Builder;
|
||||
@@ -188,10 +191,29 @@ public class NetlistBuilder {
|
||||
// sort everything alphabetically
|
||||
List<Netlist> netlists = new ArrayList<Netlist>(result.keySet());
|
||||
Collections.sort(netlists);
|
||||
netlists.forEach(netlist -> cleanupRedundantSwitches(netlist.getSwitchSetup()));
|
||||
|
||||
return netlists;
|
||||
}
|
||||
|
||||
|
||||
public static void cleanupRedundantSwitches(List<SwitchSetup> switchSetups) {
|
||||
Map<ISwitch, Set<Integer>> positionCountMap = new HashMap<>();
|
||||
switchSetups.forEach(s -> s.getPositions().forEach(pos -> {
|
||||
positionCountMap.computeIfAbsent(pos.getSwitch(), k -> new HashSet<Integer>()).add(pos.getPosition());
|
||||
}));
|
||||
// find switches that are here in all the positions, meaning that they don't affect the circuit
|
||||
Set<ISwitch> switchesToPrune = positionCountMap.entrySet().stream()
|
||||
.filter(e -> e.getKey().getPositionCount() == e.getValue().size())
|
||||
.map(x -> x.getKey())
|
||||
.collect(Collectors.toSet());
|
||||
switchSetups.forEach(s -> s.getPositions().removeIf(x -> switchesToPrune.contains(x.getSwitch())));
|
||||
switchSetups.removeIf(x -> x.getPositions().isEmpty());
|
||||
HashSet<SwitchSetup> uniqueSetups = new HashSet<>(switchSetups);
|
||||
switchSetups.clear();
|
||||
switchSetups.addAll(uniqueSetups);
|
||||
switchSetups.sort(Comparator.comparing(x -> x.toString()));
|
||||
}
|
||||
|
||||
public static Netlist buildNetlist(List<IDIYComponent<?>> components, Collection<Node> nodes,
|
||||
Collection<ContinuityArea> continuityAreas, Collection<Connection> connections) {
|
||||
ImmutableGraph<ProjectGraphNode> graph = buildGraph(nodes, continuityAreas, connections);
|
||||
|
||||
@@ -161,10 +161,10 @@ public class Tree {
|
||||
}
|
||||
|
||||
public String toAsciiString() {
|
||||
return String.join("<br>", toAscii());
|
||||
return String.join("<br>", toAsciiLines());
|
||||
}
|
||||
|
||||
private List<String> toAscii() {
|
||||
private List<String> toAsciiLines() {
|
||||
if (leaf != null) {
|
||||
return Arrays.asList(TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1) + " " + leaf.toHTML() + " " + TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1));
|
||||
}
|
||||
@@ -172,7 +172,7 @@ public class Tree {
|
||||
List<Tree> children = getOrderedChildren();
|
||||
|
||||
List<List<String>> childrenAscii =
|
||||
children.stream().map(c -> c.toAscii()).collect(Collectors.toList());
|
||||
children.stream().map(c -> c.toAsciiLines()).collect(Collectors.toList());
|
||||
|
||||
if (TreeConnectionType.Series.equals(connectionType)) {
|
||||
return TreeAsciiUtil.concatenateMultiLineSerial(TreeAsciiUtil.generateString(TreeAsciiUtil.BOXH, 1), childrenAscii);
|
||||
|
||||
@@ -54,11 +54,8 @@ import org.diylc.common.GridType;
|
||||
import org.diylc.common.IComponentFilter;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.core.BoardUndersideDisplay;
|
||||
import org.diylc.core.ComponentForRender;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IBoard;
|
||||
import org.diylc.core.IContinuity;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.ILayeredComponent;
|
||||
@@ -84,7 +81,7 @@ public class DrawingManager {
|
||||
public static int CONTROL_POINT_SIZE = 7;
|
||||
public static double EXTRA_SPACE = 0.25;
|
||||
|
||||
public static final String ZOOM_KEY = "zoom";
|
||||
public static final String ZOOM_KEY = "zoom";
|
||||
|
||||
private static boolean SHADE_EXTRA_SPACE = true;
|
||||
|
||||
@@ -93,9 +90,6 @@ public class DrawingManager {
|
||||
|
||||
public static Color CONTROL_POINT_COLOR = Color.blue;
|
||||
public static Color SELECTED_CONTROL_POINT_COLOR = Color.green;
|
||||
|
||||
public static Size BOARD_MIRROR_SPACING = new Size(0.1, SizeUnit.in);
|
||||
private static final Byte MIRRORED_ALPHA = 64;
|
||||
|
||||
private Theme theme;
|
||||
|
||||
@@ -176,14 +170,12 @@ public class DrawingManager {
|
||||
* @param visibleRect
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unlikely-arg-type")
|
||||
public List<IDIYComponent<?>> drawProject(Graphics2D g2d, Project project,
|
||||
Set<DrawOption> drawOptions, IComponentFilter filter, Rectangle selectionRect,
|
||||
Collection<IDIYComponent<?>> selectedComponents, Set<IDIYComponent<?>> lockedComponents,
|
||||
Set<IDIYComponent<?>> groupedComponents, List<Point2D> controlPointSlot,
|
||||
List<IDIYComponent<?>> componentSlot, boolean dragInProgress, Double externalZoom,
|
||||
Double scaleFactor, Rectangle2D visibleRect) {
|
||||
|
||||
long totalStartTime = System.nanoTime();
|
||||
failedComponents.clear();
|
||||
if (project == null) {
|
||||
@@ -202,35 +194,38 @@ public class DrawingManager {
|
||||
|
||||
G2DWrapper g2dWrapper = new G2DWrapper(g2d, zoom);
|
||||
|
||||
configureG2D(g2d, drawOptions);
|
||||
|
||||
// group components with boards that contain them
|
||||
Map<IBoard, List<IDIYComponent<?>>> boardMap = project.getComponents().stream()
|
||||
.filter(IBoard.class::isInstance)
|
||||
.map(IBoard.class::cast)
|
||||
.filter(b -> b.getBoardUndersideDisplay() != BoardUndersideDisplay.NONE)
|
||||
.collect(Collectors.toMap(x -> x, x -> new ArrayList<IDIYComponent<?>>()));
|
||||
Map<IDIYComponent<?>, List<IBoard>> componentBoardMap = new HashMap<IDIYComponent<?>, List<IBoard>>();
|
||||
|
||||
for (IBoard board : boardMap.keySet()) {
|
||||
Rectangle2D boardRect = board.getBoardRectangle();
|
||||
for (IDIYComponent<?> c : project.getComponents()) {
|
||||
if (c == board) {
|
||||
continue;
|
||||
}
|
||||
boolean include = true;
|
||||
for (int i = 0; i < c.getControlPointCount(); i++) {
|
||||
if (!boardRect.contains(c.getControlPoint(i))) {
|
||||
include = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (include) {
|
||||
boardMap.get(board).add(c);
|
||||
componentBoardMap.computeIfAbsent(c, key -> new ArrayList<IBoard>()).add(board);
|
||||
}
|
||||
}
|
||||
if (drawOptions.contains(DrawOption.ANTIALIASING)) {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
} else {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
|
||||
}
|
||||
if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
|
||||
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
|
||||
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
// g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
|
||||
// RenderingHints.VALUE_STROKE_PURE);
|
||||
} else {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
|
||||
RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
|
||||
RenderingHints.VALUE_COLOR_RENDER_SPEED);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
|
||||
}
|
||||
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
|
||||
RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
|
||||
|
||||
// AffineTransform initialTx = g2d.getTransform();
|
||||
Dimension canvasDimension =
|
||||
@@ -296,7 +291,6 @@ public class DrawingManager {
|
||||
if (filter != null && !filter.testComponent(component)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ComponentState state = componentStateMap.get(component);
|
||||
// Do not track the area if component is not invalidated and was
|
||||
// drawn in the same state.
|
||||
@@ -317,21 +311,15 @@ public class DrawingManager {
|
||||
long componentStart = System.nanoTime();
|
||||
try {
|
||||
|
||||
drawComponent(project, drawOptions, scaleFactor, visibleRect, zoom, g2dWrapper,
|
||||
outlineMode, i, component, state);
|
||||
|
||||
double boardMirrorSpacing = BOARD_MIRROR_SPACING.convertToPixels();
|
||||
|
||||
List<IBoard> boards = componentBoardMap.get(component);
|
||||
if (boards != null) {
|
||||
for (IBoard board : boards) {
|
||||
drawMirroredComponent(project, drawOptions, scaleFactor, visibleRect, zoom,
|
||||
g2dWrapper, outlineMode, i, state, boardMirrorSpacing, component, board);
|
||||
}
|
||||
}
|
||||
if (boardMap.keySet().contains(component)) {
|
||||
drawMirroredComponent(project, drawOptions, scaleFactor, visibleRect, zoom,
|
||||
g2dWrapper, outlineMode, i, state, boardMirrorSpacing, component, (IBoard)component);
|
||||
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache
|
||||
DrawingCache.Instance.draw(component, g2dWrapper, state,
|
||||
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect);
|
||||
else // go straight to the wrapper
|
||||
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
|
||||
if (g2dWrapper.isTrackingContinuityArea()) {
|
||||
LOG.info("Component " + component.getName() + " of type "
|
||||
+ component.getClass().getName() + " did not stop tracking continuity area.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error drawing " + component.getName(), e);
|
||||
@@ -521,114 +509,6 @@ public class DrawingManager {
|
||||
return failedComponents;
|
||||
}
|
||||
|
||||
private void drawMirroredComponent(Project project, Set<DrawOption> drawOptions,
|
||||
Double scaleFactor, Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper,
|
||||
boolean outlineMode, int i, ComponentState state, double boardMirrorSpacing,
|
||||
IDIYComponent<?> componentToDraw, IBoard board) {
|
||||
Rectangle2D boardRectangle = board.getBoardRectangle();
|
||||
AffineTransform mirrorTx = null;
|
||||
switch (board.getBoardUndersideDisplay()) {
|
||||
case ABOVE:
|
||||
mirrorTx = new AffineTransform();
|
||||
mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMinY());
|
||||
mirrorTx.scale(1, -1);
|
||||
mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMinY());
|
||||
mirrorTx.translate(0, boardMirrorSpacing);
|
||||
break;
|
||||
case BELOW:
|
||||
mirrorTx = new AffineTransform();
|
||||
mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMaxY());
|
||||
mirrorTx.scale(1, -1);
|
||||
mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMaxY());
|
||||
mirrorTx.translate(0, -boardMirrorSpacing);
|
||||
break;
|
||||
case LEFT:
|
||||
mirrorTx = new AffineTransform();
|
||||
mirrorTx.translate(boardRectangle.getMinX(), boardRectangle.getMinY());
|
||||
mirrorTx.scale(-1, 1);
|
||||
mirrorTx.translate(-boardRectangle.getMinX(), -boardRectangle.getMinY());
|
||||
mirrorTx.translate(boardMirrorSpacing, 0);
|
||||
break;
|
||||
case RIGHT:
|
||||
mirrorTx = new AffineTransform();
|
||||
mirrorTx.translate(boardRectangle.getMaxX(), boardRectangle.getMinY());
|
||||
mirrorTx.scale(-1, 1);
|
||||
mirrorTx.translate(-boardRectangle.getMaxX(), -boardRectangle.getMinY());
|
||||
mirrorTx.translate(-boardMirrorSpacing, 0);
|
||||
break;
|
||||
default:
|
||||
LOG.warn("Unexpected board underside display: " + board.getBoardUndersideDisplay());
|
||||
break;
|
||||
}
|
||||
AffineTransform txBefore = new AffineTransform(g2dWrapper.getTransform());
|
||||
byte alphaBefore = 0;
|
||||
if (AbstractTransparentComponent.class.isInstance(componentToDraw)) {
|
||||
alphaBefore = ((AbstractTransparentComponent<?>)componentToDraw).getAlpha();
|
||||
((AbstractTransparentComponent<?>)componentToDraw).setAlpha(MIRRORED_ALPHA);
|
||||
}
|
||||
try {
|
||||
g2dWrapper.setMirrorTransform(mirrorTx);
|
||||
g2dWrapper.transform(mirrorTx);
|
||||
componentToDraw.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
} finally {
|
||||
g2dWrapper.setTransform(txBefore);
|
||||
g2dWrapper.setMirrorTransform(null);
|
||||
if (AbstractTransparentComponent.class.isInstance(componentToDraw)) {
|
||||
((AbstractTransparentComponent<?>)componentToDraw).setAlpha(alphaBefore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawComponent(Project project, Set<DrawOption> drawOptions, Double scaleFactor,
|
||||
Rectangle2D visibleRect, double zoom, G2DWrapper g2dWrapper, boolean outlineMode, int i,
|
||||
IDIYComponent<?> component, ComponentState state) {
|
||||
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache
|
||||
DrawingCache.Instance.draw(component, g2dWrapper, state,
|
||||
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, scaleFactor, i, visibleRect);
|
||||
else // go straight to the wrapper
|
||||
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
|
||||
|
||||
if (g2dWrapper.isTrackingContinuityArea()) {
|
||||
LOG.debug("Component " + component.getName() + " of type "
|
||||
+ component.getClass().getName() + " did not stop tracking continuity area.");
|
||||
}
|
||||
}
|
||||
|
||||
private void configureG2D(Graphics2D g2d, Set<DrawOption> drawOptions) {
|
||||
if (drawOptions.contains(DrawOption.ANTIALIASING)) {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
} else {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
|
||||
}
|
||||
if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
|
||||
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
|
||||
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
// g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
|
||||
// RenderingHints.VALUE_STROKE_PURE);
|
||||
} else {
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
|
||||
RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
|
||||
RenderingHints.VALUE_COLOR_RENDER_SPEED);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
|
||||
}
|
||||
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
|
||||
RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
|
||||
}
|
||||
|
||||
private double zoomCached = 0;
|
||||
private Dimension canvasDimensionCached;
|
||||
private double extraSpaceCached;
|
||||
|
||||
@@ -105,8 +105,6 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
private double zoom;
|
||||
private int zOrder;
|
||||
|
||||
private AffineTransform mirrorTransform;
|
||||
|
||||
/**
|
||||
* Creates a wrapper around specified {@link Graphics2D} object.
|
||||
@@ -220,12 +218,8 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
@Override
|
||||
public void setContinuityMarker(String marker) {
|
||||
continuityMarker = marker;
|
||||
}
|
||||
|
||||
public void setMirrorTransform(AffineTransform mirrorTransform) {
|
||||
this.mirrorTransform = mirrorTransform;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends shape interior to the current component area.
|
||||
*
|
||||
@@ -350,73 +344,22 @@ class G2DWrapper extends Graphics2D implements IDrawingObserver {
|
||||
|
||||
@Override
|
||||
public void drawString(String str, int x, int y) {
|
||||
this.drawString(str, (float)x, (float)y);
|
||||
canvasGraphics.drawString(str, x, y);
|
||||
if (drawingComponent && trackingAllowed) {
|
||||
FontMetrics fontMetrics = canvasGraphics.getFontMetrics();
|
||||
Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics);
|
||||
Point2D point = new Point2D.Double(x, y);
|
||||
// currentTx.transform(point, point);
|
||||
Rectangle2D finalRec =
|
||||
new Rectangle2D.Double(rect.getX() + point.getX(), rect.getY() + point.getY(), rect.getWidth(),
|
||||
rect.getHeight());
|
||||
appendShape(finalRec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(String str, float x, float y) {
|
||||
if (this.mirrorTransform == null) {
|
||||
canvasGraphics.drawString(str, x, y);
|
||||
} else {
|
||||
drawStringUnmirrored(str, x, y);
|
||||
}
|
||||
handleDrawString(str, x, y);
|
||||
}
|
||||
|
||||
private void drawStringUnmirrored(String str, float x, float y) {
|
||||
// Save the current transform (T), which includes the mirror.
|
||||
AffineTransform originalTransform = new AffineTransform(getTransform());
|
||||
|
||||
// Compute the non-mirrored transform A by "canceling" the mirror:
|
||||
// Since T = A × M and M is self-inverse, we have A = T × M.
|
||||
AffineTransform nonMirroredTransform = new AffineTransform(originalTransform);
|
||||
nonMirroredTransform.concatenate(mirrorTransform);
|
||||
|
||||
GlyphVector gv = getFont().createGlyphVector(this.getFontRenderContext(), str);
|
||||
Rectangle2D visualBounds = gv.getVisualBounds();
|
||||
// Use a temporary Graphics2D (with transform A) to obtain correct FontMetrics.
|
||||
Graphics2D gTemp = (Graphics2D) canvasGraphics.create();
|
||||
gTemp.setTransform(nonMirroredTransform);
|
||||
FontMetrics fm = gTemp.getFontMetrics();
|
||||
double textWidth = fm.stringWidth(str);
|
||||
// Use ascent + descent as the bounding box height.
|
||||
double textHeight = visualBounds.getHeight();//fm.getAscent() + fm.getDescent();
|
||||
double ascent = fm.getAscent();
|
||||
gTemp.dispose();
|
||||
|
||||
// 1. Compute the center of the text’s bounding box in the mirrored (T) space.
|
||||
Point2D centerMirrored = new Point2D.Double(x + textWidth / 2.0,
|
||||
y + textHeight / 2.0);
|
||||
|
||||
// 2. Convert that center to screen coordinates using the original transform T.
|
||||
Point2D centerScreen = originalTransform.transform(centerMirrored, null);
|
||||
|
||||
// 3. Map the screen coordinate into the non-mirrored coordinate system (A).
|
||||
Point2D centerNonMirrored;
|
||||
try {
|
||||
centerNonMirrored = nonMirroredTransform.createInverse().transform(centerScreen, null);
|
||||
} catch (NoninvertibleTransformException e) {
|
||||
e.printStackTrace();
|
||||
centerNonMirrored = centerMirrored; // Fallback if inversion fails.
|
||||
}
|
||||
|
||||
// 4. Compute the new top-left in A so that the bounding box (width x height)
|
||||
// remains centered at centerNonMirrored.
|
||||
double newTopLeftX = centerNonMirrored.getX() - textWidth / 2.0;
|
||||
double newTopLeftY = centerNonMirrored.getY() - textHeight / 2.0;
|
||||
// Adjust for the baseline (drawString uses the baseline for y)
|
||||
float drawX = (float) newTopLeftX;
|
||||
float drawY = (float) (newTopLeftY + ascent);
|
||||
|
||||
// 5. Set the transform to the non-mirrored transform (A) so that mirror is canceled.
|
||||
canvasGraphics.setTransform(nonMirroredTransform);
|
||||
canvasGraphics.drawString(str, drawX, drawY);
|
||||
|
||||
// 6. Restore the original transform.
|
||||
setTransform(originalTransform);
|
||||
}
|
||||
|
||||
private void handleDrawString(String str, float x, float y) {
|
||||
canvasGraphics.drawString(str, x, y);
|
||||
if (drawingComponent && trackingAllowed) {
|
||||
FontMetrics fontMetrics = canvasGraphics.getFontMetrics();
|
||||
Rectangle2D rect = fontMetrics.getStringBounds(str, canvasGraphics);
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.diylc.clipboard.ComponentTransferable;
|
||||
import org.diylc.common.ComponentType;
|
||||
import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.Orientation;
|
||||
import org.diylc.common.Orientation45;
|
||||
import org.diylc.common.OrientationHV;
|
||||
import org.diylc.common.PropertyWrapper;
|
||||
import org.diylc.core.Angle;
|
||||
@@ -436,6 +437,7 @@ public class InstantiationManager {
|
||||
for (PropertyWrapper propertyWrapper : properties) {
|
||||
if (propertyWrapper.getType().getName().equals(Orientation.class.getName())
|
||||
|| propertyWrapper.getType().getName().equals(OrientationHV.class.getName())
|
||||
|| propertyWrapper.getType().getName().equals(Orientation45.class.getName())
|
||||
|| propertyWrapper.getName().equalsIgnoreCase("angle")) {
|
||||
angleProperty = propertyWrapper;
|
||||
break;
|
||||
@@ -455,6 +457,9 @@ public class InstantiationManager {
|
||||
} else if (value instanceof OrientationHV) {
|
||||
angleProperty.setValue(OrientationHV.values()[(((OrientationHV) value).ordinal() + 1)
|
||||
% OrientationHV.values().length]);
|
||||
} else if (value instanceof Orientation45) {
|
||||
angleProperty.setValue(Orientation45.values()[(((Orientation45) value).ordinal() + 1)
|
||||
% Orientation45.values().length]);
|
||||
} else if (value instanceof Angle) {
|
||||
Angle angle = (Angle) angleProperty.getValue();
|
||||
Angle newAngle = angle.rotate(1);
|
||||
|
||||
@@ -1619,8 +1619,13 @@ public class Presenter implements IPlugInPort, IConfigListener {
|
||||
}
|
||||
// There is selection, so we need to finalize the drag&drop
|
||||
// operation.
|
||||
dragInProgress = false;
|
||||
if (!preDragProject.equals(currentProject)) {
|
||||
messageDispatcher.dispatchMessage(EventType.PROJECT_MODIFIED, preDragProject, currentProject, "Drag");
|
||||
drawingManager.clearContinuityArea();
|
||||
projectFileManager.notifyFileChange();
|
||||
}
|
||||
messageDispatcher.dispatchMessage(EventType.REPAINT);
|
||||
dragInProgress = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Switched to AppImage distribution for Linux system, abandoning Flatpak as a means of distribution. DIYLC AppImage will be available alongside with other executables on the official DIYLC GitHub page.</description>
|
||||
<description>Switched to AppImage distribution for Linux system, abandoning Flatpak as a means of distribution. DIYLC AppImage will be available alongside with other executables on the official DIYLC GitHub page</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
@@ -240,5 +240,29 @@
|
||||
</changes>
|
||||
<url>https://github.com/bancika/diy-layout-creator/releases</url>
|
||||
</org.diylc.appframework.update.Version>
|
||||
<org.diylc.appframework.update.Version>
|
||||
<versionNumber>
|
||||
<major>5</major>
|
||||
<minor>1</minor>
|
||||
<build>0</build>
|
||||
</versionNumber>
|
||||
<releaseDate>2025-03-09 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>'Multimeter Probe' component added and 'Virtual Multimeter' functionality that uses it</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Cleaner text representation of switch configuration in analyzer windows - removed switches and positions that have no effect on the effective circuit</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>BUG_FIX</changeType>
|
||||
<description>Moving selected components with a mouse doesn't register file modification action and doesn't enable Undo action</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>
|
||||
|
||||
@@ -20,6 +20,17 @@ public class TreeAsciiUtilTest {
|
||||
assertEquals("yy-yyy", result.get(1));
|
||||
assertEquals(" aaa", result.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleParallel() {
|
||||
List<String> result = TreeAsciiUtil.concatenateMultiLineParallel(
|
||||
Arrays.asList(Arrays.asList("xx"), Arrays.asList("yy")));
|
||||
assertNotNull(result);
|
||||
assertEquals(3, result.size());
|
||||
assertEquals(" ┌xx┐ ", result.get(0));
|
||||
assertEquals("─┤ ├─", result.get(1));
|
||||
assertEquals(" └yy┘ ", result.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcatenateMultiLineSerial_singleInput() {
|
||||
|
||||
Binary file not shown.
@@ -34,9 +34,7 @@ import org.diylc.common.IPlugInPort;
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.VerticalAlignment;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.core.BoardUndersideDisplay;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IBoard;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
@@ -44,7 +42,7 @@ import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
public abstract class AbstractBoard extends AbstractTransparentComponent<String> implements IBoard {
|
||||
public abstract class AbstractBoard extends AbstractTransparentComponent<String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -74,8 +72,6 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
|
||||
protected Size length;
|
||||
protected Size width;
|
||||
protected BoardSizingMode mode = BoardSizingMode.TwoPoints;
|
||||
|
||||
protected BoardUndersideDisplay boardUndersideDisplay = BoardUndersideDisplay.NONE;
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
@@ -367,32 +363,6 @@ public abstract class AbstractBoard extends AbstractTransparentComponent<String>
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Underside Display")
|
||||
@Override
|
||||
public BoardUndersideDisplay getBoardUndersideDisplay() {
|
||||
if (this.boardUndersideDisplay == null) {
|
||||
this.boardUndersideDisplay = BoardUndersideDisplay.NONE;
|
||||
}
|
||||
return this.boardUndersideDisplay;
|
||||
}
|
||||
|
||||
public void setBoardUndersideDisplay(BoardUndersideDisplay boardUndersideDisplay) {
|
||||
this.boardUndersideDisplay = boardUndersideDisplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getBoardRectangle() {
|
||||
Point2D finalSecondPoint = getFinalSecondPoint();
|
||||
|
||||
return new Rectangle2D.Double(firstPoint.getX(), firstPoint.getY(),
|
||||
finalSecondPoint.getX() - firstPoint.getX(), finalSecondPoint.getY() - firstPoint.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExportToGerber() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
Point2D finalSecondPoint = getFinalSecondPoint();
|
||||
|
||||
@@ -37,11 +37,12 @@ import org.diylc.core.Project;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.gerber.IGerberBoard;
|
||||
|
||||
@ComponentDescriptor(name = "Blank Board", category = "Boards", author = "Branislav Stojkovic",
|
||||
zOrder = IDIYComponent.BOARD, instanceNamePrefix = "Board", description = "Blank circuit board",
|
||||
bomPolicy = BomPolicy.SHOW_ONLY_TYPE_NAME, autoEdit = false, transformer = SimpleComponentTransformer.class)
|
||||
public class BlankBoard extends AbstractBoard {
|
||||
public class BlankBoard extends AbstractBoard implements IGerberBoard {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -131,11 +132,6 @@ public class BlankBoard extends AbstractBoard {
|
||||
public String getControlPointNodeName(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExportToGerber() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum Type {
|
||||
ROUND, SQUARE;
|
||||
@@ -145,4 +141,12 @@ public class BlankBoard extends AbstractBoard {
|
||||
return name().substring(0, 1) + name().substring(1).toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getBoardRectangle() {
|
||||
Point2D finalSecondPoint = getFinalSecondPoint();
|
||||
|
||||
return new Rectangle2D.Double(firstPoint.getX(), firstPoint.getY(),
|
||||
finalSecondPoint.getX() - firstPoint.getX(), finalSecondPoint.getY() - firstPoint.getY());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
*
|
||||
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
|
||||
*
|
||||
* This file is part of DIYLC.
|
||||
*
|
||||
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.diylc.components.connectivity;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import org.diylc.common.ObjectCache;
|
||||
import org.diylc.common.Orientation45;
|
||||
import org.diylc.components.AbstractTransparentComponent;
|
||||
import org.diylc.components.transform.SimpleComponentTransformer;
|
||||
import org.diylc.core.ComponentState;
|
||||
import org.diylc.core.IDIYComponent;
|
||||
import org.diylc.core.IDrawingObserver;
|
||||
import org.diylc.core.Project;
|
||||
import org.diylc.core.VisibilityPolicy;
|
||||
import org.diylc.core.annotations.BomPolicy;
|
||||
import org.diylc.core.annotations.ComponentDescriptor;
|
||||
import org.diylc.core.annotations.EditableProperty;
|
||||
import org.diylc.core.annotations.KeywordPolicy;
|
||||
import org.diylc.core.measures.Size;
|
||||
import org.diylc.core.measures.SizeUnit;
|
||||
|
||||
@ComponentDescriptor(name = "Multimeter Probe", category = "Connectivity",
|
||||
author = "Branislav Stojkovic", description = "Multimeter Probe", instanceNamePrefix = "Probe",
|
||||
zOrder = IDIYComponent.TEXT, bomPolicy = BomPolicy.NEVER_SHOW, autoEdit = false,
|
||||
keywordPolicy = KeywordPolicy.SHOW_TYPE_NAME, transformer = SimpleComponentTransformer.class,
|
||||
enableCache = true)
|
||||
public class MultimeterProbe extends AbstractTransparentComponent<Color> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static Size PROBE_DIAMETER = new Size(0.05d, SizeUnit.in);
|
||||
public static Size PROBE_LENGTH = new Size(0.4d, SizeUnit.in);
|
||||
public static Color PROBE_COLOR = METAL_COLOR;
|
||||
public static Size HANDLE_DIAMETER = new Size(0.2d, SizeUnit.in);
|
||||
public static Size HANDLE_LENGTH = new Size(0.4d, SizeUnit.in);
|
||||
public static Color HANDLE_COLOR = Color.red;
|
||||
|
||||
private Point2D.Double point = new Point2D.Double(0, 0);
|
||||
private Color handleColor = HANDLE_COLOR;
|
||||
private Orientation45 orientation = Orientation45._315;
|
||||
|
||||
private transient Shape[] body = null;
|
||||
|
||||
public MultimeterProbe() {
|
||||
this.alpha = (byte) (MAX_ALPHA / 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode,
|
||||
Project project, IDrawingObserver drawingObserver) {
|
||||
|
||||
Shape[] shapes = getBody();
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1f));
|
||||
|
||||
if (!outlineMode) {
|
||||
Composite oldComposite = g2d.getComposite();
|
||||
if (alpha < MAX_ALPHA) {
|
||||
g2d.setComposite(
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f * alpha / MAX_ALPHA));
|
||||
}
|
||||
g2d.setColor(PROBE_COLOR);
|
||||
g2d.fill(shapes[0]);
|
||||
g2d.setColor(handleColor);
|
||||
g2d.fill(shapes[1]);
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: handleColor.darker());
|
||||
g2d.draw(shapes[1]);
|
||||
g2d.setColor(
|
||||
componentState == ComponentState.SELECTED || componentState == ComponentState.DRAGGING
|
||||
? SELECTION_COLOR
|
||||
: PROBE_COLOR.darker());
|
||||
g2d.draw(shapes[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawIcon(Graphics2D g2d, int width, int height) {
|
||||
double probeDiameter = 2;
|
||||
double probeLength = height / 2 + 2;
|
||||
double handleDiameter = width / 4;
|
||||
double handleLength = height / 2 + 2;
|
||||
double x = 2;
|
||||
double y = height - 2;
|
||||
Shape probe = buildProbeShape(probeDiameter, probeLength, x, y);
|
||||
Shape handle = buildHandleShape(probeDiameter, probeLength, handleDiameter, handleLength, x, y);
|
||||
|
||||
double theta = Math.PI / 4;
|
||||
AffineTransform tx = AffineTransform.getRotateInstance(theta, x, y);
|
||||
probe = tx.createTransformedShape(probe);
|
||||
handle = tx.createTransformedShape(handle);
|
||||
|
||||
g2d.setColor(PROBE_COLOR);
|
||||
g2d.fill(probe);
|
||||
g2d.setColor(HANDLE_COLOR);
|
||||
g2d.fill(handle);
|
||||
|
||||
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(1));
|
||||
g2d.setColor(PROBE_COLOR.darker());
|
||||
g2d.draw(probe);
|
||||
g2d.setColor(HANDLE_COLOR.darker());
|
||||
g2d.draw(handle);
|
||||
}
|
||||
|
||||
public Shape[] getBody() {
|
||||
if (body == null) {
|
||||
double probeDiameter = PROBE_DIAMETER.convertToPixels();
|
||||
double probeLength = PROBE_LENGTH.convertToPixels();
|
||||
double handleDiameter = HANDLE_DIAMETER.convertToPixels();
|
||||
double handleLength = HANDLE_LENGTH.convertToPixels();
|
||||
double x = point.getX();
|
||||
double y = point.getY();
|
||||
body = new Shape[2];
|
||||
Path2D probe = buildProbeShape(probeDiameter, probeLength, x, y);
|
||||
body[0] = probe;
|
||||
Path2D handle =
|
||||
buildHandleShape(probeDiameter, probeLength, handleDiameter, handleLength, x, y);
|
||||
body[1] = handle;
|
||||
|
||||
double theta = orientation.toRadians();
|
||||
if (theta != 0) {
|
||||
AffineTransform tx = AffineTransform.getRotateInstance(theta, x, y);
|
||||
body[0] = tx.createTransformedShape(body[0]);
|
||||
body[1] = tx.createTransformedShape(body[1]);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
private Path2D buildHandleShape(double probeDiameter, double probeLength, double handleDiameter,
|
||||
double handleLength, double x, double y) {
|
||||
Path2D handle = new Path2D.Double();
|
||||
handle.moveTo(x + probeDiameter, y - probeLength);
|
||||
handle.lineTo(x + handleDiameter / 2, y - handleLength - probeLength);
|
||||
handle.lineTo(x - handleDiameter / 2, y - handleLength - probeLength);
|
||||
handle.lineTo(x - probeDiameter, y - probeLength);
|
||||
handle.moveTo(x + probeDiameter, y - probeLength);
|
||||
handle.closePath();
|
||||
return handle;
|
||||
}
|
||||
|
||||
private Path2D buildProbeShape(double probeDiameter, double probeLength, double x, double y) {
|
||||
Path2D probe = new Path2D.Double();
|
||||
probe.moveTo(x, y);
|
||||
probe.lineTo(x - probeDiameter / 2, y - probeDiameter);
|
||||
probe.lineTo(x - probeDiameter / 2, y - probeLength);
|
||||
probe.lineTo(x + probeDiameter / 2, y - probeLength);
|
||||
probe.lineTo(x + probeDiameter / 2, y - probeDiameter);
|
||||
probe.lineTo(x, y);
|
||||
probe.closePath();
|
||||
return probe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getControlPointCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControlPointSticky(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
|
||||
return VisibilityPolicy.NEVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2D getControlPoint(int index) {
|
||||
return point;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlPoint(Point2D point, int index) {
|
||||
this.point.setLocation(point);
|
||||
this.body = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@EditableProperty(name = "Color")
|
||||
public Color getValue() {
|
||||
return handleColor;
|
||||
}
|
||||
|
||||
public void setValue(Color color) {
|
||||
this.handleColor = color;
|
||||
}
|
||||
|
||||
@EditableProperty(name = "Angle")
|
||||
public Orientation45 getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public void setOrientation(Orientation45 orientation) {
|
||||
this.orientation = orientation;
|
||||
this.body = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
return "Tip";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getCachingBounds() {
|
||||
Shape[] shapes = getBody();
|
||||
Rectangle2D bounds1 = shapes[0].getBounds2D();
|
||||
Rectangle2D bounds2 = shapes[1].getBounds2D();
|
||||
double minX = Math.min(bounds1.getMinX(), bounds2.getMinX());
|
||||
double minY = Math.min(bounds1.getMinY(), bounds2.getMinY());
|
||||
double maxX = Math.max(bounds1.getMaxX(), bounds2.getMaxX());
|
||||
double maxY = Math.max(bounds1.getMaxY(), bounds2.getMaxY());
|
||||
return new Rectangle2D.Double(minX - 1, minY - 1, maxX - minX + 2, maxY - minY + 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
*
|
||||
* DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors.
|
||||
*
|
||||
* This file is part of DIYLC.
|
||||
*
|
||||
* DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License as published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with DIYLC. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.diylc.netlist;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.swing.JLabel;
|
||||
import org.diylc.common.INetlistAnalyzer;
|
||||
import org.diylc.components.connectivity.MultimeterProbe;
|
||||
|
||||
public class MultimeterAnalyzer extends NetlistAnalyzer implements INetlistAnalyzer {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Virtual Multimeter";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return "multimeter";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconName() {
|
||||
return "Multimeter";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFontName() {
|
||||
return new JLabel().getFont().getName();
|
||||
}
|
||||
|
||||
protected Summary summarize(Netlist netlist, Node preferredOutput) throws TreeException {
|
||||
Tree tree = constructTree(netlist);
|
||||
|
||||
if (tree == null) {
|
||||
return new Summary(netlist, "No components found connecting the two multimeter probes.");
|
||||
}
|
||||
|
||||
List<String> notes = new ArrayList<String>();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
String asciiTree = tree.toAsciiString();
|
||||
|
||||
sb.append("<br><br>")
|
||||
.append("<font face='Courier New'>")
|
||||
.append(asciiTree)
|
||||
.append("</font>");
|
||||
if (!notes.isEmpty()) {
|
||||
Collections.sort(notes);
|
||||
sb.append("<br><br>");
|
||||
for (String v : notes) {
|
||||
sb.append(v).append("<br>");
|
||||
}
|
||||
}
|
||||
|
||||
return new Summary(netlist, sb.toString());
|
||||
}
|
||||
|
||||
public Tree constructTree(Netlist netlist) throws TreeException {
|
||||
List<Node> probeNodes = find(Set.of(MultimeterProbe.class.getCanonicalName()), null, netlist);
|
||||
|
||||
if (probeNodes.size() == 0) {
|
||||
throw new TreeException("No multimeter probes are placed on the circuit.");
|
||||
} else if (probeNodes.size() != 2) {
|
||||
throw new TreeException(
|
||||
"Place exactly two multimeter probes on the circuit in order to run the analysis.");
|
||||
} else {
|
||||
return constructTreeBetween(netlist, probeNodes.get(0), probeNodes.get(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
@@ -17,18 +17,22 @@
|
||||
10 throws / 10 positions
|
||||
11 throws / 11 positions
|
||||
12 throws / 12 positions
|
||||
135 degrees clockwise
|
||||
1P12T
|
||||
2 throws / 2 positions
|
||||
2 throws / 3 positions / OFF even positions
|
||||
225 degrees clockwise
|
||||
2P5T
|
||||
2P6T
|
||||
3 throws / 3 positions
|
||||
3 throws / 5 positions / SHORT even positions
|
||||
315 degrees clockwise
|
||||
3P4T
|
||||
3PDT
|
||||
3PDT (On/Off/On)
|
||||
4 band
|
||||
4 throws / 4 positions
|
||||
45 degrees clockwise
|
||||
4P3T
|
||||
4P5T
|
||||
4P5T (Super/Mega)
|
||||
@@ -52,15 +56,10 @@ AWG
|
||||
Alternate
|
||||
And
|
||||
Angle Offset
|
||||
Apply Model
|
||||
B-C-E BJT
|
||||
Background
|
||||
Board1
|
||||
Box
|
||||
C-B-E BJT
|
||||
C1
|
||||
C2
|
||||
C3
|
||||
Circle
|
||||
Coil(s)
|
||||
Component reordering is possible only when Project Explorer is sorted by z-index.
|
||||
@@ -177,7 +176,7 @@ Ten
|
||||
Terminal
|
||||
Timing
|
||||
Tubular Beveled
|
||||
Underside Display
|
||||
Virtual Multimeter
|
||||
Xnor
|
||||
Xor
|
||||
cm
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -69,7 +69,7 @@ public enum IconLoader {
|
||||
SnapToComponents("snap_to_components.png"), SnapToNone("snap_to_none.png"), TraceProximity("trace_proximity.png"),
|
||||
ComponentLarge("component_large.png"), ImportNetlist("import_netlist.png"), SplashIC("splash_ic.png"),
|
||||
ComponentPreferences("component_preferences.png"), ComponentReplace("component_replace.png"), Barcode("barcode.png"),
|
||||
DocumentGerber("document_gerber.png"), DocumentX2("document_x2.png"), Export("export.png");
|
||||
Multimeter("multimeter.png"), DocumentGerber("document_gerber.png"), DocumentX2("document_x2.png"), Export("export.png");
|
||||
|
||||
protected String name;
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 864 B |
Reference in New Issue
Block a user