Improved precision of rendering

This commit is contained in:
Bane Stojkovic
2026-06-10 17:19:21 +02:00
parent 9179086bd5
commit 1d7dd87d85
57 changed files with 1505 additions and 1460 deletions
@@ -386,8 +386,8 @@ public class DrawingManager {
g2dWrapper.setColor(CONTROL_POINT_COLOR);
Point2D controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE - 2;
g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2),
(int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize);
g2dWrapper.fillOval((int) Math.round(controlPoint.getX() - pointSize / 2),
(int) Math.round(controlPoint.getY() - pointSize / 2), pointSize, pointSize);
}
}
}
@@ -403,11 +403,11 @@ public class DrawingManager {
int pointSize = CONTROL_POINT_SIZE;
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2),
(int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize);
g2dWrapper.fillOval((int) Math.round(controlPoint.getX() - pointSize / 2),
(int) Math.round(controlPoint.getY() - pointSize / 2), pointSize, pointSize);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval((int) (controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1),
(int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
g2dWrapper.fillOval((int) Math.round(controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1),
(int) Math.round(controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
CONTROL_POINT_SIZE - 2);
}
}
@@ -437,12 +437,12 @@ public class DrawingManager {
for (Point2D point : controlPointSlot) {
if (point != null) {
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2),
(int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE,
g2dWrapper.fillOval((int) Math.round(point.getX() - CONTROL_POINT_SIZE / 2),
(int) Math.round(point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE,
CONTROL_POINT_SIZE);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2 + 1),
(int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
g2dWrapper.fillOval((int) Math.round(point.getX() - CONTROL_POINT_SIZE / 2 + 1),
(int) Math.round(point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
CONTROL_POINT_SIZE - 2);
}
}
@@ -1289,8 +1289,8 @@ public class Presenter implements IPlugInPort {
// If there's no selection, the only thing to do is update the
// selection rectangle and refresh.
Rectangle oldSelectionRect = selectionRect == null ? null : new Rectangle(selectionRect);
this.selectionRect = Utils.createRectangle(new Point((int)scaledPoint.getX(), (int)scaledPoint.getY()),
new Point((int)previousDragPoint.getX(), (int)previousDragPoint.getY()));
this.selectionRect = Utils.createRectangle(new Point((int) Math.round(scaledPoint.getX()), (int) Math.round(scaledPoint.getY())),
new Point((int) Math.round(previousDragPoint.getX()), (int) Math.round(previousDragPoint.getY())));
if (selectionRect.equals(oldSelectionRect)) {
return true;
}
@@ -1621,8 +1621,8 @@ public class Presenter implements IPlugInPort {
// If there's no selection finalize selectionRect and see which
// components intersect with it.
if (scaledPoint != null && previousDragPoint != null) {
this.selectionRect = Utils.createRectangle(new Point((int)scaledPoint.getX(), (int)scaledPoint.getY()),
new Point((int)previousDragPoint.getX(), (int)previousDragPoint.getY()));;
this.selectionRect = Utils.createRectangle(new Point((int) Math.round(scaledPoint.getX()), (int) Math.round(scaledPoint.getY())),
new Point((int) Math.round(previousDragPoint.getX()), (int) Math.round(previousDragPoint.getY())));;
}
List<IDIYComponent<?>> newSelection = new ArrayList<IDIYComponent<?>>();
if (operationMode != OperationMode.HIGHLIGHT_CONNECTED_AREAS)
@@ -32,6 +32,8 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import org.diylc.utils.MathUtils;
/**
* Serializes {@link Area} objects by converting coordinates from pixels to inches, thus avoiding
* the resolution to affect point placement.
@@ -66,7 +68,7 @@ public class AreaConverter implements Converter {
}
for (int i = 0; i < n; i++) {
writer.startNode("coordinate");
writer.setValue(String.valueOf(coord[i]));
writer.setValue(MathUtils.formatDouble(coord[i]));
writer.endNode();
}
writer.endNode();
@@ -87,7 +89,7 @@ public class AreaConverter implements Converter {
List<java.lang.Double> values = new ArrayList<java.lang.Double>();
while (reader.hasMoreChildren()) {
reader.moveDown();
values.add(java.lang.Double.parseDouble(reader.getValue()));
values.add(MathUtils.parseDouble(reader.getValue()));
reader.moveUp();
}
java.lang.Double[] coord = values.toArray(new java.lang.Double[0]);
@@ -30,6 +30,7 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import org.diylc.utils.Constants;
import org.diylc.utils.MathUtils;
/**
* Serializes {@link Point2D} objects by converting coordinates from pixels to inches, thus avoiding
@@ -42,26 +43,27 @@ public class PointConverter implements Converter {
@Override
public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) {
Point2D point = (Point2D) object;
writer.addAttribute("x", Double.toString(1d * point.getX() / Constants.PIXELS_PER_INCH));
writer.addAttribute("y", Double.toString(1d * point.getY() / Constants.PIXELS_PER_INCH));
writer.addAttribute("x", MathUtils.formatDouble(1d * point.getX() / Constants.PIXELS_PER_INCH));
writer.addAttribute("y", MathUtils.formatDouble(1d * point.getY() / Constants.PIXELS_PER_INCH));
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
double x;
double y;
if (reader.hasMoreChildren()) {
reader.moveDown();
x = Double.parseDouble(reader.getValue());
x = MathUtils.parseDouble(reader.getValue());
reader.moveUp();
reader.moveDown();
y = Double.parseDouble(reader.getValue());
y = MathUtils.parseDouble(reader.getValue());
reader.moveUp();
return new Point2D.Double(x, y);
} else {
x = Double.parseDouble(reader.getAttribute("x"));
y = Double.parseDouble(reader.getAttribute("y"));
return new Point2D.Double(x * Constants.PIXELS_PER_INCH, y * Constants.PIXELS_PER_INCH);
x = MathUtils.round(Double.parseDouble(reader.getAttribute("x")) * Constants.PIXELS_PER_INCH);
y = MathUtils.round(Double.parseDouble(reader.getAttribute("y")) * Constants.PIXELS_PER_INCH);
return new Point2D.Double(x, y);
}
}
@@ -0,0 +1,37 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 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.utils;
public class MathUtils {
public static String formatDouble(double value) {
return Double.toString(Math.round(value * 1e8) / 1e8);
}
public static Double round(double value) {
return Math.round(value * 1e8) / 1e8;
}
public static double parseDouble(String str) {
return Math.round(Double.parseDouble(str) * 1e8) / 1e8;
}
}
@@ -397,6 +397,10 @@
<changeType>NEW_FEATURE</changeType>
<description>Detect the style of a treble bleed based on the topology and component values (e.g. Fender, DiMarzio, Kinman, etc)</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>IMPROVEMENT</changeType>
<description>Improved precision of rendering, in some cases there was a sub-pixel misalignment between the lines that should meet exactly</description>
</org.diylc.appframework.update.Change>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Tone capacitor mistaken for treble bleed when connected to the input lug of the volume pot</description>
@@ -1,173 +1,173 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 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.testcomponents;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import org.diylc.components.AbstractComponent;
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.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.measures.Capacitance;
import org.diylc.core.measures.CapacitanceUnit;
import org.diylc.core.measures.Resistance;
import org.diylc.core.measures.ResistanceUnit;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
@ComponentDescriptor(name = "Mock", author = "bancika", category = "Sample", instanceNamePrefix = "M",
description = "test", zOrder = IDIYComponent.COMPONENT)
public class MockDIYComponent extends AbstractComponent<Capacitance> {
private static final long serialVersionUID = 1L;
private String testField = "Hello World";
private Capacitance c = new Capacitance(100d, CapacitanceUnit.uF);
private Resistance r = new Resistance(123d, ResistanceUnit.K);
private Size s = new Size(1d, SizeUnit.cm);
private Color color = Color.green;
private Point2D leftTopCorner = new Point(0, 0);
private String name = "something";
public MockDIYComponent(String testField, Capacitance c, Resistance r, Size s, Color color, Point leftTopCorner) {
super();
this.testField = testField;
this.c = c;
this.r = r;
this.s = s;
this.color = color;
this.leftTopCorner = leftTopCorner;
}
public MockDIYComponent() {
super();
}
@Override
public boolean canControlPointOverlap(int index) {
return false;
}
@EditableProperty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int getControlPointCount() {
return 1;
}
@Override
public boolean isControlPointSticky(int index) {
return false;
}
@Override
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
return VisibilityPolicy.WHEN_SELECTED;
}
@Override
public Point2D getControlPoint(int index) {
return leftTopCorner;
}
@Override
public void setControlPoint(Point2D point, int index) {
leftTopCorner.setLocation(point);
}
@EditableProperty(name = "Test Field")
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
@EditableProperty(defaultable = true)
public Capacitance getValue() {
return c;
}
public void setValue(Capacitance c) {
this.c = c;
}
@EditableProperty(defaultable = true)
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
@EditableProperty
public Resistance getR() {
return r;
}
// Setter is commented out, ClassProcessor should ignore this property.
// public void setR(Resistance r) {
// this.r = r;
// }
// No annotation, so this property should be ignored.
public Size getS() {
return s;
}
public void setS(Size s) {
this.s = s;
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
g2d.setColor(componentState.equals(ComponentState.SELECTED) ? color : color.darker());
g2d.fillRect((int)leftTopCorner.getX(), (int)leftTopCorner.getY(), 200, 50);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.drawString("X", 10, 10);
}
@Override
public String getControlPointNodeName(int index) {
return null;
}
}
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 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.testcomponents;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D;
import org.diylc.components.AbstractComponent;
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.ComponentDescriptor;
import org.diylc.core.annotations.EditableProperty;
import org.diylc.core.measures.Capacitance;
import org.diylc.core.measures.CapacitanceUnit;
import org.diylc.core.measures.Resistance;
import org.diylc.core.measures.ResistanceUnit;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
@ComponentDescriptor(name = "Mock", author = "bancika", category = "Sample", instanceNamePrefix = "M",
description = "test", zOrder = IDIYComponent.COMPONENT)
public class MockDIYComponent extends AbstractComponent<Capacitance> {
private static final long serialVersionUID = 1L;
private String testField = "Hello World";
private Capacitance c = new Capacitance(100d, CapacitanceUnit.uF);
private Resistance r = new Resistance(123d, ResistanceUnit.K);
private Size s = new Size(1d, SizeUnit.cm);
private Color color = Color.green;
private Point2D leftTopCorner = new Point(0, 0);
private String name = "something";
public MockDIYComponent(String testField, Capacitance c, Resistance r, Size s, Color color, Point leftTopCorner) {
super();
this.testField = testField;
this.c = c;
this.r = r;
this.s = s;
this.color = color;
this.leftTopCorner = leftTopCorner;
}
public MockDIYComponent() {
super();
}
@Override
public boolean canControlPointOverlap(int index) {
return false;
}
@EditableProperty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int getControlPointCount() {
return 1;
}
@Override
public boolean isControlPointSticky(int index) {
return false;
}
@Override
public VisibilityPolicy getControlPointVisibilityPolicy(int index) {
return VisibilityPolicy.WHEN_SELECTED;
}
@Override
public Point2D getControlPoint(int index) {
return leftTopCorner;
}
@Override
public void setControlPoint(Point2D point, int index) {
leftTopCorner.setLocation(point);
}
@EditableProperty(name = "Test Field")
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
@EditableProperty(defaultable = true)
public Capacitance getValue() {
return c;
}
public void setValue(Capacitance c) {
this.c = c;
}
@EditableProperty(defaultable = true)
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
@EditableProperty
public Resistance getR() {
return r;
}
// Setter is commented out, ClassProcessor should ignore this property.
// public void setR(Resistance r) {
// this.r = r;
// }
// No annotation, so this property should be ignored.
public Size getS() {
return s;
}
public void setS(Size s) {
this.s = s;
}
@Override
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
IDrawingObserver drawingObserver) {
g2d.setColor(componentState.equals(ComponentState.SELECTED) ? color : color.darker());
g2d.fillRect((int) Math.round(leftTopCorner.getX()), (int) Math.round(leftTopCorner.getY()), 200, 50);
}
@Override
public void drawIcon(Graphics2D g2d, int width, int height) {
g2d.drawString("X", 10, 10);
}
@Override
public String getControlPointNodeName(int index) {
return null;
}
}