diff --git a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java index b4e3c55a..600d6eed 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java +++ b/diylc/diylc-core/src/org/diylc/presenter/DrawingManager.java @@ -34,6 +34,7 @@ import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -108,7 +109,7 @@ public class DrawingManager { private boolean debugComponentAreas; private boolean debugContinuityAreas; - + // rendering stats private Map renderStatsByType = new HashMap(); private long lastStatsReportedTime = System.currentTimeMillis(); @@ -119,28 +120,30 @@ public class DrawingManager { private List proximityMarkers = null; - public DrawingManager(MessageDispatcher messageDispatcher, IConfigurationManager configManager) { + public DrawingManager(MessageDispatcher messageDispatcher, + IConfigurationManager configManager) { super(); this.messageDispatcher = messageDispatcher; this.configManager = configManager; - + try { - this.theme = (Theme) configManager.readObject(IPlugInPort.THEME_KEY, - Constants.DEFAULT_THEME); + this.theme = (Theme) configManager.readObject(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME); } catch (Exception e) { LOG.error("Error loading theme", e); this.theme = Constants.DEFAULT_THEME; // replace bad value with default configManager.writeValue(IPlugInPort.THEME_KEY, Constants.DEFAULT_THEME); } - + componentAreaMap = new HashMap, ComponentArea>(); lastDrawnStateMap = new HashMap, ComponentState>(); 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); - debugContinuityAreas = debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true"); + debugContinuityAreas = + debugContinuityAreasStr != null && debugContinuityAreasStr.equalsIgnoreCase("true"); } /** @@ -161,10 +164,12 @@ public class DrawingManager { * @param visibleRect * @return */ - public List> drawProject(Graphics2D g2d, Project project, Set drawOptions, - IComponentFilter filter, Rectangle selectionRect, Collection> selectedComponents, - Set> lockedComponents, Set> groupedComponents, List controlPointSlot, - List> componentSlot, boolean dragInProgress, Double externalZoom, Rectangle2D visibleRect) { + public List> drawProject(Graphics2D g2d, Project project, + Set drawOptions, IComponentFilter filter, Rectangle selectionRect, + Collection> selectedComponents, Set> lockedComponents, + Set> groupedComponents, List controlPointSlot, + List> componentSlot, boolean dragInProgress, Double externalZoom, + Rectangle2D visibleRect) { long totalStartTime = System.nanoTime(); failedComponents.clear(); if (project == null) { @@ -185,104 +190,69 @@ public class DrawingManager { 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); + 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); + 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_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_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_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_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); } // 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.fillRect(0, 0, d.width, d.height); - g2d.clip(new Rectangle(new Point(0, 0), d)); - + g2dWrapper.fillRect(0, 0, canvasDimension.width, canvasDimension.height); + g2d.clip(new Rectangle(new Point(0, 0), canvasDimension)); + + Rectangle2D extraSpaceRect = new Rectangle2D.Double(); + AffineTransform extraSpaceTx = new AffineTransform(); + // calculate size to be rendered double extraSpace = getExtraSpace(project) * zoom; - Dimension dInner = getCanvasDimensions(project, zoom, false); - GridType gridType = GridType.LINES; - 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)); - } - - 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()); - } + drawGrid(project, g2d, zoom, extraSpace, canvasDimension, visibleRect, drawOptions, + extraSpaceRect, extraSpaceTx); // apply zoom if (Math.abs(1.0 - zoom) > 1e-4) { g2dWrapper.scale(zoom, zoom); if (visibleRect != null) - visibleRect.setRect(visibleRect.getX() / zoom, visibleRect.getY() / zoom, - visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom); + visibleRect.setRect(visibleRect.getX() / zoom, visibleRect.getY() / zoom, + visibleRect.getWidth() / zoom, visibleRect.getHeight() / zoom); } - + // Composite mainComposite = g2d.getComposite(); // Composite alphaComposite = // AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); // g2dWrapper.resetTx(); - + // put components and their states in a map - Map, ComponentState> componentStateMap = new HashMap, ComponentState>(); + Map, ComponentState> componentStateMap = + new HashMap, ComponentState>(); for (IDIYComponent component : project.getComponents()) { ComponentState state = ComponentState.NORMAL; if (drawOptions.contains(DrawOption.SELECTION) && selectedComponents.contains(component)) { @@ -294,9 +264,9 @@ public class DrawingManager { } componentStateMap.put(component, state); } - + boolean outlineMode = drawOptions.contains(DrawOption.OUTLINE_MODE); - + // give the cache a chance to prepare all the components in multiple threads if (drawOptions.contains(DrawOption.ENABLE_CACHING)) { DrawingCache.Instance.bulkPrepare(componentStateMap, g2dWrapper, outlineMode, project, zoom); @@ -312,7 +282,7 @@ public class DrawingManager { // Do not track the area if component is not invalidated and was // drawn in the same state. boolean trackArea = lastDrawnStateMap.get(component) != state; - + synchronized (g2d) { g2dWrapper.startedDrawingComponent(); if (!trackArea) { @@ -326,14 +296,16 @@ public class DrawingManager { // Draw the component through the g2dWrapper. long componentStart = System.nanoTime(); try { - - if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache - DrawingCache.Instance.draw(component, g2dWrapper, state, drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, visibleRect); + + if (drawOptions.contains(DrawOption.ENABLE_CACHING)) // go through the DrawingCache + DrawingCache.Instance.draw(component, g2dWrapper, state, + drawOptions.contains(DrawOption.OUTLINE_MODE), project, zoom, 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."); + 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); @@ -349,7 +321,7 @@ public class DrawingManager { Counter stats = null; String key = component.getClass().getCanonicalName().replace("org.diylc.components.", ""); if (renderStatsByType.containsKey(key)) { - stats = renderStatsByType.get(key); + stats = renderStatsByType.get(key); } else { stats = new Counter(); renderStatsByType.put(key, stats); @@ -374,13 +346,17 @@ public class DrawingManager { continue; VisibilityPolicy visibilityPolicy = component.getControlPointVisibilityPolicy(i); if ((groupedComponents.contains(component) - && (visibilityPolicy == VisibilityPolicy.ALWAYS || (selectedComponents.contains(component) && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) || (!groupedComponents - .contains(component) && !selectedComponents.contains(component) && component - .getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) { + && (visibilityPolicy == VisibilityPolicy.ALWAYS + || (selectedComponents.contains(component) + && visibilityPolicy == VisibilityPolicy.WHEN_SELECTED)) + || (!groupedComponents.contains(component) + && !selectedComponents.contains(component) + && component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS))) { 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) (controlPoint.getX() - pointSize / 2), + (int) (controlPoint.getY() - pointSize / 2), pointSize, pointSize); } } } @@ -389,17 +365,19 @@ public class DrawingManager { for (IDIYComponent component : selectedComponents) { for (int i = 0; i < component.getControlPointCount(); i++) { if (!groupedComponents.contains(component) - && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED || component - .getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) { + && (component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.WHEN_SELECTED + || component.getControlPointVisibilityPolicy(i) == VisibilityPolicy.ALWAYS)) { Point2D controlPoint = component.getControlPoint(i); 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) (controlPoint.getX() - pointSize / 2), + (int) (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, CONTROL_POINT_SIZE - 2); + g2dWrapper.fillOval((int) (controlPoint.getX() - CONTROL_POINT_SIZE / 2 + 1), + (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) { try { - component.draw(g2dWrapper, ComponentState.NORMAL, drawOptions.contains(DrawOption.OUTLINE_MODE), project, - g2dWrapper); + component.draw(g2dWrapper, ComponentState.NORMAL, + drawOptions.contains(DrawOption.OUTLINE_MODE), project, g2dWrapper); } catch (Exception e) { LOG.error("Error drawing " + component.getName(), e); @@ -428,11 +406,13 @@ 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) (point.getX() - CONTROL_POINT_SIZE / 2), + (int) (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, CONTROL_POINT_SIZE - 2); + g2dWrapper.fillOval((int) (point.getX() - CONTROL_POINT_SIZE / 2 + 1), + (int) (point.getY() - CONTROL_POINT_SIZE / 2 + 1), CONTROL_POINT_SIZE - 2, + CONTROL_POINT_SIZE - 2); } } } @@ -477,9 +457,8 @@ public class DrawingManager { g2d.draw(a); } } - - if (this.proximityMarkers != null) - { + + if (this.proximityMarkers != null) { g2d.setStroke(ObjectCache.getInstance().fetchBasicStroke(2)); g2d.setColor(Color.red); for (Shape s : this.proximityMarkers) @@ -493,33 +472,125 @@ public class DrawingManager { g2d.fill(currentContinuityArea); g2d.setComposite(oldComposite); } - + // shade extra space 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)); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.05f)); g2d.setTransform(extraSpaceTx); g2d.setColor(theme.getOutlineColor()); g2d.fill(extraSpaceArea); } - + long totalEndTime = System.nanoTime(); - + totalStats.add(totalEndTime - totalStartTime); - + logStats(); 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 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() { // log render time stats periodically if (System.currentTimeMillis() - lastStatsReportedTime > statReportFrequencyMs) { lastStatsReportedTime = System.currentTimeMillis(); - String mapAsString = renderStatsByType.entrySet().stream().sorted((e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) - .map(e -> e.toString()) - .collect(Collectors.joining("; ", "{", "}")); + String mapAsString = renderStatsByType.entrySet().stream() + .sorted( + (e1, e2) -> -Long.compare(e1.getValue().getNanoTime(), e2.getValue().getNanoTime())) + .map(e -> e.toString()).collect(Collectors.joining("; ", "{", "}")); LOG.debug("Render stats: " + mapAsString); LOG.debug("Page stats: " + totalStats.toAvgString()); DrawingCache.Instance.logStats(); @@ -572,11 +643,13 @@ public class DrawingManager { public double getExtraSpace(Project project) { double width = project.getWidth().convertToPixels(); double height = project.getHeight().convertToPixels(); - double targetExtraSpace = EXTRA_SPACE * Math.max(width, height); - return CalcUtils.roundToGrid(targetExtraSpace, project.getGridSpacing().scale(project.getDotSpacing())); + double targetExtraSpace = EXTRA_SPACE * Math.max(width, height); + 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 height = project.getHeight().convertToPixels(); @@ -588,7 +661,7 @@ public class DrawingManager { width *= zoomLevel; height *= zoomLevel; - + return new Dimension((int) width, (int) height); } @@ -606,7 +679,7 @@ public class DrawingManager { configManager.writeValue(IPlugInPort.THEME_KEY, theme); messageDispatcher.dispatchMessage(EventType.REPAINT); } - + public void findContinuityAreaAtPoint(Project project, Point2D p) { if (continuityAreaCache == null) continuityAreaCache = getContinuityAreas(project); @@ -620,7 +693,7 @@ public class DrawingManager { currentContinuityArea = null; } - + public List getContinuityAreas(Project project) { // Find all individual continuity areas for all components List preliminaryAreas = new ArrayList(); @@ -628,11 +701,11 @@ public class DrawingManager { Set connections = new HashSet(); for (IDIYComponent c : project.getComponents()) { ComponentArea a = getComponentArea(c); - + if (c instanceof IContinuity) { for (int i = 0; i < c.getControlPointCount() - 1; i++) for (int j = i + 1; j < c.getControlPointCount(); j++) - if (((IContinuity)c).arePointsConnected(i, j)) + if (((IContinuity) c).arePointsConnected(i, j)) connections.add(new Connection(c.getControlPoint(i), c.getControlPoint(j))); } @@ -654,30 +727,32 @@ public class DrawingManager { checkBreakout.set(i, true); } } - } + } } // Check if we need to break some areas out in case they are interrupted List areas = new ArrayList(); for (int i = 0; i < preliminaryAreas.size(); i++) { Area a = preliminaryAreas.get(i); - if (checkBreakout.get(i)) - areas.addAll(AreaUtils.tryAreaBreakout(a)); - else - areas.add(a); + if (checkBreakout.get(i)) + areas.addAll(AreaUtils.tryAreaBreakout(a)); + else + areas.add(a); } - + CalcUtils.expandConnections(connections); AreaUtils.crunchAreas(areas, connections); - + return areas; } - + public List getContinuityAreaProximity(Project project, float threshold) { - List continuityAreas = getContinuityAreas(project); - Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for approx good results - List expanded = continuityAreas.parallelStream().map((a) -> new Area(s.createStrokedShape(a))).collect(Collectors.toList()); + List continuityAreas = getContinuityAreas(project); + Stroke s = ObjectCache.getInstance().fetchBasicStroke(threshold - 1); // value eyeballed for + // approx good results + List expanded = continuityAreas.parallelStream() + .map((a) -> new Area(s.createStrokedShape(a))).collect(Collectors.toList()); List intersections = new ArrayList(); for (int i = 0; i < expanded.size() - 1; i++) { for (int j = i + 1; j < expanded.size(); j++) { @@ -688,25 +763,28 @@ public class DrawingManager { intersections.add(first); } } - + AreaUtils.crunchAreas(intersections, null); double minSize = new Size(1d, SizeUnit.cm).convertToPixels(); - + List shapes = new ArrayList(); for (Area a : intersections) { Rectangle2D bounds2d = a.getBounds2D(); 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) - 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()))); + 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()))); } - - //AreaUtils.crunchAreas(shapes, null); - - this.proximityMarkers = shapes; - + + // AreaUtils.crunchAreas(shapes, null); + + this.proximityMarkers = shapes; + return shapes; } } diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml index 869037ce..9205a56c 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/update.xml +++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml @@ -3801,5 +3801,21 @@ https://github.com/bancika/diy-layout-creator/releases + + + 4 + 22 + 3 + + 2022-03-18 00:00:00.000 CET + + + + BUG_FIX + Improved rendering performance + + + https://github.com/bancika/diy-layout-creator/releases + diff --git a/diylc/diylc-library/lib/diylc-core.jar b/diylc/diylc-library/lib/diylc-core.jar index c2978555..4b644471 100644 Binary files a/diylc/diylc-library/lib/diylc-core.jar and b/diylc/diylc-library/lib/diylc-core.jar differ diff --git a/diylc/diylc-library/src/org/diylc/components/guitar/AbstractGuitarPickup.java b/diylc/diylc-library/src/org/diylc/components/guitar/AbstractGuitarPickup.java index 93a52990..c3a02f7a 100644 --- a/diylc/diylc-library/src/org/diylc/components/guitar/AbstractGuitarPickup.java +++ b/diylc/diylc-library/src/org/diylc/components/guitar/AbstractGuitarPickup.java @@ -23,10 +23,8 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; -import java.awt.geom.Area; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; - import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.awt.StringUtils; import org.diylc.common.HorizontalAlignment; @@ -259,14 +257,26 @@ public abstract class AbstractGuitarPickup extends AbstractLabeledComponent 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 { diff --git a/diylc/diylc-library/src/org/diylc/components/passive/PotentiometerPanel.java b/diylc/diylc-library/src/org/diylc/components/passive/PotentiometerPanel.java index 17b832cf..57d5e1ce 100644 --- a/diylc/diylc-library/src/org/diylc/components/passive/PotentiometerPanel.java +++ b/diylc/diylc-library/src/org/diylc/components/passive/PotentiometerPanel.java @@ -27,6 +27,7 @@ import java.awt.Composite; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.Shape; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; @@ -477,14 +478,26 @@ public class PotentiometerPanel extends AbstractPotentiometer { @Override public Rectangle2D getCachingBounds() { - Area area = new Area(); - Area[] body = getBody(); + Shape[] body = getBody(); int margin = 20; - for (Area a : body) - if (a != null) - area.add(a); - Rectangle2D bounds = area.getBounds2D(); - return new Rectangle2D.Double(bounds.getX() - margin, bounds.getY() - margin, bounds.getWidth() + 2 * margin, bounds.getHeight() + 2 * margin); + double minX = 0; + double minY = 0; + double maxX = 0; + double maxY = 0; + 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 diff --git a/diylc/diylc-library/src/org/diylc/components/semiconductors/TransistorTO3.java b/diylc/diylc-library/src/org/diylc/components/semiconductors/TransistorTO3.java index e8179f76..7b86d8e9 100644 --- a/diylc/diylc-library/src/org/diylc/components/semiconductors/TransistorTO3.java +++ b/diylc/diylc-library/src/org/diylc/components/semiconductors/TransistorTO3.java @@ -28,6 +28,7 @@ import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; +import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; @@ -367,13 +368,25 @@ public class TransistorTO3 extends AbstractLabeledComponent { @Override public Rectangle2D getCachingBounds() { - Area area = new Area(); - Area[] body = getBody(); + Shape[] body = getBody(); int margin = 20; - for (Area a : body) - if (a != null) - area.add(a); - Rectangle2D bounds = area.getBounds2D(); - return new Rectangle2D.Double(bounds.getX() - margin, bounds.getY() - margin, bounds.getWidth() + 2 * margin, bounds.getHeight() + 2 * margin); + double minX = 0; + double minY = 0; + double maxX = 0; + double maxY = 0; + 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); } } diff --git a/diylc/diylc-swing/RegressionTestRunner.launch b/diylc/diylc-swing/RegressionTestRunner.launch index 5bc0594e..027dba42 100644 --- a/diylc/diylc-swing/RegressionTestRunner.launch +++ b/diylc/diylc-swing/RegressionTestRunner.launch @@ -9,6 +9,6 @@ - + diff --git a/diylc/diylc-swing/lib/diylc-core.jar b/diylc/diylc-swing/lib/diylc-core.jar index c2978555..4b644471 100644 Binary files a/diylc/diylc-swing/lib/diylc-core.jar and b/diylc/diylc-swing/lib/diylc-core.jar differ diff --git a/diylc/diylc-swing/library/main.jar b/diylc/diylc-swing/library/main.jar index 712abd9b..0c9ef0ec 100644 Binary files a/diylc/diylc-swing/library/main.jar and b/diylc/diylc-swing/library/main.jar differ