Improve speed of rendering of the grid

This commit is contained in:
Bane Stojkovic
2022-03-18 20:55:40 +01:00
parent bf89b07cea
commit d50acb4911
9 changed files with 297 additions and 167 deletions
@@ -34,6 +34,7 @@ import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D; import java.awt.geom.Line2D;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@@ -119,14 +120,14 @@ public class DrawingManager {
private List<Area> proximityMarkers = null; private List<Area> proximityMarkers = null;
public DrawingManager(MessageDispatcher<EventType> messageDispatcher, IConfigurationManager<?> configManager) { public DrawingManager(MessageDispatcher<EventType> messageDispatcher,
IConfigurationManager<?> configManager) {
super(); super();
this.messageDispatcher = messageDispatcher; this.messageDispatcher = messageDispatcher;
this.configManager = configManager; this.configManager = configManager;
try { try {
this.theme = (Theme) configManager.readObject(IPlugInPort.THEME_KEY, this.theme = (Theme) configManager.readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME);
Constants.DEFAULT_THEME);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error loading theme", e); LOG.error("Error loading theme", e);
this.theme = Constants.DEFAULT_THEME; this.theme = Constants.DEFAULT_THEME;
@@ -137,10 +138,12 @@ public class DrawingManager {
componentAreaMap = new HashMap<IDIYComponent<?>, ComponentArea>(); componentAreaMap = new HashMap<IDIYComponent<?>, ComponentArea>();
lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>(); lastDrawnStateMap = new HashMap<IDIYComponent<?>, ComponentState>();
String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS); String debugComponentAreasStr = System.getProperty(DEBUG_COMPONENT_AREAS);
debugComponentAreas = debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true"); debugComponentAreas =
debugComponentAreasStr != null && debugComponentAreasStr.equalsIgnoreCase("true");
String debugContinuityAreasStr = System.getProperty(DEBUG_CONTINUITY_AREAS); String debugContinuityAreasStr = System.getProperty(DEBUG_CONTINUITY_AREAS);
debugContinuityAreas = debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true"); debugContinuityAreas =
debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true");
} }
/** /**
@@ -161,10 +164,12 @@ public class DrawingManager {
* @param visibleRect * @param visibleRect
* @return * @return
*/ */
public List<IDIYComponent<?>> drawProject(Graphics2D g2d, Project project, Set<DrawOption> drawOptions, public List<IDIYComponent<?>> drawProject(Graphics2D g2d, Project project,
IComponentFilter filter, Rectangle selectionRect, Collection<IDIYComponent<?>> selectedComponents, Set<DrawOption> drawOptions, IComponentFilter filter, Rectangle selectionRect,
Set<IDIYComponent<?>> lockedComponents, Set<IDIYComponent<?>> groupedComponents, List<Point2D> controlPointSlot, Collection<IDIYComponent<?>> selectedComponents, Set<IDIYComponent<?>> lockedComponents,
List<IDIYComponent<?>> componentSlot, boolean dragInProgress, Double externalZoom, Rectangle2D visibleRect) { Set<IDIYComponent<?>> groupedComponents, List<Point2D> controlPointSlot,
List<IDIYComponent<?>> componentSlot, boolean dragInProgress, Double externalZoom,
Rectangle2D visibleRect) {
long totalStartTime = System.nanoTime(); long totalStartTime = System.nanoTime();
failedComponents.clear(); failedComponents.clear();
if (project == null) { if (project == null) {
@@ -185,87 +190,51 @@ public class DrawingManager {
if (drawOptions.contains(DrawOption.ANTIALIASING)) { if (drawOptions.contains(DrawOption.ANTIALIASING)) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
} else { } else {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
} }
if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) { if (configManager.readBoolean(IPlugInPort.HI_QUALITY_RENDER_KEY, false)) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 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_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, // g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
// RenderingHints.VALUE_STROKE_PURE); // RenderingHints.VALUE_STROKE_PURE);
} else { } else {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED); 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_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
} }
// AffineTransform initialTx = g2d.getTransform(); // AffineTransform initialTx = g2d.getTransform();
Dimension d = getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE)); Dimension canvasDimension =
getCanvasDimensions(project, zoom, drawOptions.contains(DrawOption.EXTRA_SPACE));
g2dWrapper.setColor(theme.getBgColor()); g2dWrapper.setColor(theme.getBgColor());
g2dWrapper.fillRect(0, 0, d.width, d.height); g2dWrapper.fillRect(0, 0, canvasDimension.width, canvasDimension.height);
g2d.clip(new Rectangle(new Point(0, 0), d)); g2d.clip(new Rectangle(new Point(0, 0), canvasDimension));
Rectangle2D extraSpaceRect = new Rectangle2D.Double();
AffineTransform extraSpaceTx = new AffineTransform();
// calculate size to be rendered // calculate size to be rendered
double extraSpace = getExtraSpace(project) * zoom; double extraSpace = getExtraSpace(project) * zoom;
Dimension dInner = getCanvasDimensions(project, zoom, false);
GridType gridType = GridType.LINES; drawGrid(project, g2d, zoom, extraSpace, canvasDimension, visibleRect, drawOptions,
if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) { extraSpaceRect, extraSpaceTx);
double zoomStep = project.getGridSpacing().convertToPixels() * zoom;
float gridThickness = (float) (1f * (zoom > 1 ? 1 : zoom));
if (gridType == GridType.CROSSHAIR) {
g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[] {
(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4));
} else if (gridType == GridType.DOT) {
g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[] {
1f, (float) zoomStep - 1}, 0f));
} else {
g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness));
}
g2dWrapper.setColor(theme.getGridColor());
for (double i = zoomStep; i < d.width; i += zoomStep) {
g2dWrapper.draw(new Line2D.Double(i, 0, i, d.height - 1));
}
for (double j = zoomStep; j < d.height; j += zoomStep) {
g2dWrapper.draw(new Line2D.Double(0, j, d.width - 1, j));
}
// draw dots if needed
if (project.getDotSpacing() > 1 && zoomStep > 8) {
g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness * 3));
g2d.setColor(theme.getDotColor());
for (double i = extraSpace + zoomStep * project.getDotSpacing(); i < extraSpace + dInner.width; i += zoomStep * project.getDotSpacing())
for (double j = extraSpace + zoomStep * project.getDotSpacing(); j < extraSpace + dInner.height; j += zoomStep * project.getDotSpacing()) {
g2d.fillOval((int)Math.round(i - 1), (int)Math.round(j - 1), 3, 3);
}
}
}
Rectangle2D extraSpaceRect = null;
AffineTransform extraSpaceTx = null;
// manage extra space
if (drawOptions.contains(DrawOption.EXTRA_SPACE)) {
float borderThickness = (float) (3f * (zoom > 1 ? 1 : zoom));
g2d.setStroke(ObjectCache.getInstance().fetchStroke(borderThickness, new float[] {borderThickness * 4, borderThickness * 4, }, 0, BasicStroke.CAP_BUTT));
g2dWrapper.setColor(theme.getOutlineColor());
extraSpaceRect = new Rectangle2D.Double(extraSpace, extraSpace, dInner.getWidth(), dInner.getHeight());
g2d.draw(extraSpaceRect);
extraSpaceTx = g2d.getTransform();
// translate to the new (0, 0)
g2d.transform(AffineTransform.getTranslateInstance(extraSpace, extraSpace));
if (visibleRect != null)
visibleRect.setRect(visibleRect.getX() - extraSpace, visibleRect.getY() - extraSpace,
visibleRect.getWidth(), visibleRect.getHeight());
}
// apply zoom // apply zoom
if (Math.abs(1.0 - zoom) > 1e-4) { if (Math.abs(1.0 - zoom) > 1e-4) {
@@ -282,7 +251,8 @@ public class DrawingManager {
// g2dWrapper.resetTx(); // g2dWrapper.resetTx();
// put components and their states in a map // put components and their states in a map
Map<IDIYComponent<?>, ComponentState> componentStateMap = new HashMap<IDIYComponent<?>, ComponentState>(); Map<IDIYComponent<?>, ComponentState> componentStateMap =
new HashMap<IDIYComponent<?>, ComponentState>();
for (IDIYComponent<?> component : project.getComponents()) { for (IDIYComponent<?> component : project.getComponents()) {
ComponentState state = ComponentState.NORMAL; ComponentState state = ComponentState.NORMAL;
if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) { if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) {
@@ -328,12 +298,14 @@ public class DrawingManager {
try { try {
if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache
DrawingCache.Instance.draw(component, g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, visibleRect); DrawingCache.Instance.draw(component, g2dWrapper, state,
drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, visibleRect);
else // go straight to the wrapper else // go straight to the wrapper
component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper); component.draw(g2dWrapper, state, outlineMode, project, g2dWrapper);
if (g2dWrapper.isTrackingContinuityArea()) { if (g2dWrapper.isTrackingContinuityArea()) {
LOG.info("Component " + component.getName() + " of type " + component.getClass().getName() + " did not stop tracking continuity area."); LOG.info("Component " + component.getName() + " of type "
+ component.getClass().getName() + " did not stop tracking continuity area.");
} }
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e); LOG.error("Error drawing " + component.getName(), e);
@@ -374,13 +346,17 @@ public class DrawingManager {
continue; continue;
VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i); VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i);
if ((groupedComponents.contains(component) if ((groupedComponents.contains(component)
&& (visibilityPolicy == VisibilityPolicy.ALWAYS || (selectedComponents.contains(component) && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) || (!groupedComponents && (visibilityPolicy == VisibilityPolicy.ALWAYS
.contains(component) && !selectedComponents.contains(component) && component || (selectedComponents.contains(component)
.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) { && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED))
|| (!groupedComponents.contains(component)
&& !selectedComponents.contains(component)
&& component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) {
g2dWrapper.setColor(CONTROL_POINT_COLOR); g2dWrapper.setColor(CONTROL_POINT_COLOR);
Point2D controlPoint = component.getControlPoint(i); Point2D controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE - 2; int pointSize = CONTROL_POINT_SIZE - 2;
g2dWrapper.fillOval((int)(controlPoint.getX() - pointSize / 2), (int)(controlPoint.getY() - pointSize / 2), pointSize, pointSize); g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2),
(int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize);
} }
} }
} }
@@ -389,17 +365,19 @@ public class DrawingManager {
for (IDIYComponent<?> component : selectedComponents) { for (IDIYComponent<?> component : selectedComponents) {
for (int i = 0; i < component.getControlPointCount(); i++) { for (int i = 0; i < component.getControlPointCount(); i++) {
if (!groupedComponents.contains(component) if (!groupedComponents.contains(component)
&& (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED || component && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED
.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) { || component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) {
Point2D controlPoint = component.getControlPoint(i); Point2D controlPoint = component.getControlPoint(i);
int pointSize = CONTROL_POINT_SIZE; int pointSize = CONTROL_POINT_SIZE;
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker());
g2dWrapper.fillOval((int)(controlPoint.getX() - pointSize / 2), (int)(controlPoint.getY() - pointSize / 2), pointSize, pointSize); g2dWrapper.fillOval((int) (controlPoint.getX() - pointSize / 2),
(int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval((int)(controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1), (int)(controlPoint.getY() - CONTROL_POINT_SIZE / 2 g2dWrapper.fillOval((int) (controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1),
+ 1), CONTROL_POINT_SIZE - 2, CONTROL_POINT_SIZE - 2); (int) (controlPoint.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
CONTROL_POINT_SIZE - 2);
} }
} }
} }
@@ -412,8 +390,8 @@ public class DrawingManager {
for (IDIYComponent<?> component : componentSlot) { for (IDIYComponent<?> component : componentSlot) {
try { try {
component.draw(g2dWrapper, ComponentState.NORMAL, drawOptions.contains(DrawOption.OUTLINE_MODE), project, component.draw(g2dWrapper, ComponentState.NORMAL,
g2dWrapper); drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error drawing " + component.getName(), e); LOG.error("Error drawing " + component.getName(), e);
@@ -428,11 +406,13 @@ public class DrawingManager {
for (Point2D point : controlPointSlot) { for (Point2D point : controlPointSlot) {
if (point != null) { if (point != null) {
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR.darker()); 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) (point.getX() - CONTROL_POINT_SIZE / 2),
(int) (point.getY() - CONTROL_POINT_SIZE / 2), CONTROL_POINT_SIZE,
CONTROL_POINT_SIZE); CONTROL_POINT_SIZE);
g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR); g2dWrapper.setColor(SELECTED_CONTROL_POINT_COLOR);
g2dWrapper.fillOval((int)(point.getX() - CONTROL_POINT_SIZE / 2 + 1), (int)(point.getY() - CONTROL_POINT_SIZE / 2 + 1), g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2 + 1),
CONTROL_POINT_SIZE - 2, CONTROL_POINT_SIZE - 2); (int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2,
CONTROL_POINT_SIZE - 2);
} }
} }
} }
@@ -478,8 +458,7 @@ public class DrawingManager {
} }
} }
if (this.proximityMarkers != null) if (this.proximityMarkers != null) {
{
g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2)); g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2));
g2d.setColor(Color.red); g2d.setColor(Color.red);
for (Shape s : this.proximityMarkers) for (Shape s : this.proximityMarkers)
@@ -496,7 +475,8 @@ public class DrawingManager {
// shade extra space // shade extra space
if (SHADE_EXTRA_SPACE && extraSpaceRect != null) { if (SHADE_EXTRA_SPACE && extraSpaceRect != null) {
Area extraSpaceArea = new Area(new Rectangle2D.Double(0, 0, d.getWidth(), d.getHeight())); Area extraSpaceArea = new Area(
new Rectangle2D.Double(0, 0, canvasDimension.getWidth(), canvasDimension.getHeight()));
extraSpaceArea.subtract(new Area(extraSpaceRect)); extraSpaceArea.subtract(new Area(extraSpaceRect));
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.05f)); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.05f));
g2d.setTransform(extraSpaceTx); g2d.setTransform(extraSpaceTx);
@@ -513,13 +493,104 @@ public class DrawingManager {
return failedComponents; return failedComponents;
} }
private double zoomCached = 0;
private Dimension canvasDimensionCached;
private double extraSpaceCached;
private GridType gridTypeCached;
private BufferedImage cachedGridImage;
private void drawGrid(Project project, Graphics2D g2dIn, double zoom, double extraSpace,
Dimension canvasDimension, Rectangle2D visibleRect, Set<DrawOption> drawOptions,
Rectangle2D extraSpaceRect, AffineTransform extraSpaceTx) {
GridType gridType = GridType.LINES;
Dimension innerCanvasDimension = getCanvasDimensions(project, zoom, false);
// draw from cache
if (zoom != zoomCached || !canvasDimension.equals(canvasDimensionCached)
|| extraSpace != extraSpaceCached || gridType != gridTypeCached) {
try {
cachedGridImage = new BufferedImage((int) canvasDimension.getWidth(),
(int) canvasDimension.getHeight(), BufferedImage.TYPE_INT_ARGB);
// create graphics
Graphics2D g2d = cachedGridImage.createGraphics();
if (drawOptions.contains(DrawOption.GRID) && gridType != GridType.NONE) {
double zoomStep = project.getGridSpacing().convertToPixels() * zoom;
float gridThickness = (float) (1f * (zoom > 1 ? 1 : zoom));
if (gridType == GridType.CROSSHAIR) {
g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10f,
new float[] {(float) zoomStep / 2, (float) zoomStep / 2}, (float) zoomStep / 4));
} else if (gridType == GridType.DOT) {
g2d.setStroke(new BasicStroke(gridThickness, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10f, new float[] {1f, (float) zoomStep - 1}, 0f));
} else {
g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness));
}
g2d.setColor(theme.getGridColor());
for (double i = zoomStep; i < canvasDimension.width; i += zoomStep) {
g2d.draw(new Line2D.Double(i, 0, i, canvasDimension.height - 1));
}
for (double j = zoomStep; j < canvasDimension.height; j += zoomStep) {
g2d.draw(new Line2D.Double(0, j, canvasDimension.width - 1, j));
}
// draw dots if needed
if (project.getDotSpacing() > 1 && zoomStep > 8) {
g2d.setStroke(ObjectCache.getInstance().fetchZoomableStroke(gridThickness * 3));
g2d.setColor(theme.getDotColor());
for (double i = extraSpace + zoomStep * project.getDotSpacing(); i < extraSpace
+ innerCanvasDimension.width; i += zoomStep * project.getDotSpacing())
for (double j = extraSpace + zoomStep * project.getDotSpacing(); j < extraSpace
+ innerCanvasDimension.height; j += zoomStep * project.getDotSpacing()) {
g2d.fillOval((int) Math.round(i - 1), (int) Math.round(j - 1), 3, 3);
}
}
}
} finally {
zoomCached = zoom;
canvasDimensionCached = (Dimension) canvasDimension.clone();
extraSpaceCached = extraSpace;
gridTypeCached = gridType;
}
}
if (cachedGridImage == null) {
LOG.warn("Cached grid image is null!");
return;
}
g2dIn.drawImage(cachedGridImage, 0, 0, null);
// manage extra space
if (drawOptions.contains(DrawOption.EXTRA_SPACE)) {
float borderThickness = (float) (3f * (zoom > 1 ? 1 : zoom));
g2dIn.setStroke(ObjectCache.getInstance().fetchStroke(borderThickness,
new float[] {borderThickness * 4, borderThickness * 4,}, 0, BasicStroke.CAP_BUTT));
g2dIn.setColor(theme.getOutlineColor());
extraSpaceRect.setRect(new Rectangle2D.Double(extraSpace, extraSpace,
innerCanvasDimension.getWidth(), innerCanvasDimension.getHeight()));
g2dIn.draw(extraSpaceRect);
extraSpaceTx.setTransform(g2dIn.getTransform());
// translate to the new (0, 0)
g2dIn.transform(AffineTransform.getTranslateInstance(extraSpace, extraSpace));
if (visibleRect != null)
visibleRect.setRect(visibleRect.getX() - extraSpace, visibleRect.getY() - extraSpace,
visibleRect.getWidth(), visibleRect.getHeight());
}
}
public void logStats() { public void logStats() {
// log render time stats periodically // log render time stats periodically
if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) { if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) {
lastStatsReportedTime = System.currentTimeMillis(); lastStatsReportedTime = System.currentTimeMillis();
String mapAsString = renderStatsByType.entrySet().stream().sorted((e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) String mapAsString = renderStatsByType.entrySet().stream()
.map(e -> e.toString()) .sorted(
.collect(Collectors.joining("; ", "{", "}")); (e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime()))
.map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}"));
LOG.debug("Render stats: " + mapAsString); LOG.debug("Render stats: " + mapAsString);
LOG.debug("Page stats: " + totalStats.toAvgString()); LOG.debug("Page stats: " + totalStats.toAvgString());
DrawingCache.Instance.logStats(); DrawingCache.Instance.logStats();
@@ -573,10 +644,12 @@ public class DrawingManager {
double width = project.getWidth().convertToPixels(); double width = project.getWidth().convertToPixels();
double height = project.getHeight().convertToPixels(); double height = project.getHeight().convertToPixels();
double targetExtraSpace = EXTRA_SPACE * Math.max(width, height); double targetExtraSpace = EXTRA_SPACE * Math.max(width, height);
return CalcUtils.roundToGrid(targetExtraSpace, project.getGridSpacing().scale(project.getDotSpacing())); return CalcUtils.roundToGrid(targetExtraSpace,
project.getGridSpacing().scale(project.getDotSpacing()));
} }
public Dimension getCanvasDimensions(Project project, Double zoomLevel, boolean includeExtraSpace) { public Dimension getCanvasDimensions(Project project, Double zoomLevel,
boolean includeExtraSpace) {
double width = project.getWidth().convertToPixels(); double width = project.getWidth().convertToPixels();
double height = project.getHeight().convertToPixels(); double height = project.getHeight().convertToPixels();
@@ -676,8 +749,10 @@ public class DrawingManager {
public List<Area> getContinuityAreaProximity(Project project, float threshold) { public List<Area> getContinuityAreaProximity(Project project, float threshold) {
List<Area> continuityAreas = getContinuityAreas(project); List<Area> continuityAreas = getContinuityAreas(project);
Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for approx good results Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for
List<Area> expanded = continuityAreas.parallelStream().map((a) -> new Area(s.createStrokedShape(a))).collect(Collectors.toList()); // approx good results
List<Area> expanded = continuityAreas.parallelStream()
.map((a) -> new Area(s.createStrokedShape(a))).collect(Collectors.toList());
List<Area> intersections = new ArrayList<Area>(); List<Area> intersections = new ArrayList<Area>();
for (int i = 0; i < expanded.size() - 1; i++) { for (int i = 0; i < expanded.size() - 1; i++) {
for (int j = i + 1; j < expanded.size(); j++) { for (int j = i + 1; j < expanded.size(); j++) {
@@ -696,11 +771,14 @@ public class DrawingManager {
for (Area a : intersections) { for (Area a : intersections) {
Rectangle2D bounds2d = a.getBounds2D(); Rectangle2D bounds2d = a.getBounds2D();
if (bounds2d.getWidth() < minSize) if (bounds2d.getWidth() < minSize)
bounds2d = new Rectangle2D.Double(bounds2d.getCenterX() - minSize / 2, bounds2d.getY(), minSize, bounds2d.getHeight()); bounds2d = new Rectangle2D.Double(bounds2d.getCenterX() - minSize / 2, bounds2d.getY(),
minSize, bounds2d.getHeight());
if (bounds2d.getHeight() < minSize) if (bounds2d.getHeight() < minSize)
bounds2d = new Rectangle2D.Double(bounds2d.getX(), bounds2d.getCenterY() - minSize / 2, bounds2d.getWidth(), minSize); bounds2d = new Rectangle2D.Double(bounds2d.getX(), bounds2d.getCenterY() - minSize / 2,
bounds2d.getWidth(), minSize);
shapes.add(new Area(new Ellipse2D.Double(bounds2d.getX(), bounds2d.getY(), bounds2d.getWidth(), bounds2d.getHeight()))); shapes.add(new Area(new Ellipse2D.Double(bounds2d.getX(), bounds2d.getY(),
bounds2d.getWidth(), bounds2d.getHeight())));
} }
// AreaUtils.crunchAreas(shapes, null); // AreaUtils.crunchAreas(shapes, null);
@@ -3801,5 +3801,21 @@
</changes> </changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url> <url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version> </org.diylc.appframework.update.Version>
<org.diylc.appframework.update.Version>
<versionNumber>
<major>4</major>
<minor>22</minor>
<build>3</build>
</versionNumber>
<releaseDate>2022-03-18 00:00:00.000 CET</releaseDate>
<name></name>
<changes>
<org.diylc.appframework.update.Change>
<changeType>BUG_FIX</changeType>
<description>Improved rendering performance</description>
</org.diylc.appframework.update.Change>
</changes>
<url>https://github.com/bancika/diy-layout-creator/releases</url>
</org.diylc.appframework.update.Version>
</a> </a>
</java.util.Arrays_-ArrayList> </java.util.Arrays_-ArrayList>
Binary file not shown.
@@ -23,10 +23,8 @@ import java.awt.Graphics2D;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape; import java.awt.Shape;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.awt.StringUtils; import org.diylc.awt.StringUtils;
import org.diylc.common.HorizontalAlignment; import org.diylc.common.HorizontalAlignment;
@@ -259,14 +257,26 @@ public abstract class AbstractGuitarPickup extends AbstractLabeledComponent<Stri
@Override @Override
public Rectangle2D getCachingBounds() { public Rectangle2D getCachingBounds() {
Area area = new Area();
Shape[] body = getBody(); Shape[] body = getBody();
int margin = 20; int margin = 20;
for (Shape a : body) double minX = 0;
if (a != null) double minY = 0;
area.add(new Area(a)); double maxX = 0;
Rectangle2D bounds = area.getBounds2D(); double maxY = 0;
return new Rectangle2D.Double(bounds.getX() - margin, bounds.getY() - margin, bounds.getWidth() + 2 * margin, bounds.getHeight() + 2 * margin); for (Shape a : body) {
if (a != null) {
Rectangle2D bounds2d = a.getBounds2D();
if (bounds2d.getMinX() < minX)
minX = bounds2d.getMinX();
if (bounds2d.getMinY() < minY)
minY = bounds2d.getMinY();
if (bounds2d.getMaxX() > maxX)
maxX = bounds2d.getMaxX();
if (bounds2d.getMaxY() > maxY)
maxY = bounds2d.getMaxY();
}
}
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin);
} }
public enum Polarity { public enum Polarity {
@@ -27,6 +27,7 @@ import java.awt.Composite;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Polygon; import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.Area; import java.awt.geom.Area;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
@@ -477,14 +478,26 @@ public class PotentiometerPanel extends AbstractPotentiometer {
@Override @Override
public Rectangle2D getCachingBounds() { public Rectangle2D getCachingBounds() {
Area area = new Area(); Shape[] body = getBody();
Area[] body = getBody();
int margin = 20; int margin = 20;
for (Area a : body) double minX = 0;
if (a != null) double minY = 0;
area.add(a); double maxX = 0;
Rectangle2D bounds = area.getBounds2D(); double maxY = 0;
return new Rectangle2D.Double(bounds.getX() - margin, bounds.getY() - margin, bounds.getWidth() + 2 * margin, bounds.getHeight() + 2 * margin); for (Shape a : body) {
if (a != null) {
Rectangle2D bounds2d = a.getBounds2D();
if (bounds2d.getMinX() < minX)
minX = bounds2d.getMinX();
if (bounds2d.getMinY() < minY)
minY = bounds2d.getMinY();
if (bounds2d.getMaxX() > maxX)
maxX = bounds2d.getMaxX();
if (bounds2d.getMaxY() > maxY)
maxY = bounds2d.getMaxY();
}
}
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin);
} }
@Override @Override
@@ -28,6 +28,7 @@ import java.awt.FontMetrics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.RenderingHints; import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Area; import java.awt.geom.Area;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
@@ -367,13 +368,25 @@ public class TransistorTO3 extends AbstractLabeledComponent<String> {
@Override @Override
public Rectangle2D getCachingBounds() { public Rectangle2D getCachingBounds() {
Area area = new Area(); Shape[] body = getBody();
Area[] body = getBody();
int margin = 20; int margin = 20;
for (Area a : body) double minX = 0;
if (a != null) double minY = 0;
area.add(a); double maxX = 0;
Rectangle2D bounds = area.getBounds2D(); double maxY = 0;
return new Rectangle2D.Double(bounds.getX() - margin, bounds.getY() - margin, bounds.getWidth() + 2 * margin, bounds.getHeight() + 2 * margin); for (Shape a : body) {
if (a != null) {
Rectangle2D bounds2d = a.getBounds2D();
if (bounds2d.getMinX() < minX)
minX = bounds2d.getMinX();
if (bounds2d.getMinY() < minY)
minY = bounds2d.getMinY();
if (bounds2d.getMaxX() > maxX)
maxX = bounds2d.getMaxX();
if (bounds2d.getMaxY() > maxY)
maxY = bounds2d.getMaxY();
}
}
return new Rectangle2D.Double(minX - margin, minY - margin, maxX - minX + 2 * margin, maxY - minY + 2 * margin);
} }
} }
@@ -9,6 +9,6 @@
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/> <booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.diylc.RegressionTestRunner"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.diylc.RegressionTestRunner"/>
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="diylc-swing"/> <stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="diylc-swing"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="&quot;C:\Users\bancika\Documents\GitHub\diy-layout-creator\diylc-regression-data&quot; TEST travelcaster3"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="&quot;C:\Users\bancika\Documents\GitHub\diy-layout-creator\diylc-regression-data&quot; TEST diy-fever"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="diylc-swing"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="diylc-swing"/>
</launchConfiguration> </launchConfiguration>
Binary file not shown.
Binary file not shown.