Lines is a Nanoleaf shape we haven't been able to test earlier.
Now we have tested them, and added support for painting them as well.
They do unfortunately not support touch gestures.
Signed-off-by: Jørgen Austvik <jaustvik@acm.org>
| Shapes Mini Triangles | NL48 | Mini Triangles | X | X |
| Elements Hexagon | NL52 | Elements Hexagons | X | X |
| Smart Bulb | NL45 | Smart Bulb | - | |
-| Lightstrip | NL55 | Lightstrip | - | |
-| Lines | NL59 | Lines | - | |
+| Lightstrip | NL55 | Lightstrip | - | - |
+| Lines | NL59 | Lines | X | |
| Canvas | NL29 | Squares | X | X |
x = Supported (-) = unknown (no device available to test)
SHAPES_CONTROLLER("Controller (Shapes)", 12, 0, 0, 1, DrawingAlgorithm.NONE),
ELEMENTS_HEXAGON("Elements Hexagon", 14, 134, 6, 1, DrawingAlgorithm.HEXAGON),
ELEMENTS_HEXAGON_CORNER("Elements Hexagon - Corner", 15, 58, 6, 6, DrawingAlgorithm.CORNER),
- LINES_CONNECTOR("Lines Connector", 16, 11, 1, 1, DrawingAlgorithm.LINE),
+ LINES_CONNECTOR("Lines Connector", 16, 11, 1, 1, DrawingAlgorithm.NONE),
LIGHT_LINES("Light Lines", 17, 154, 1, 1, DrawingAlgorithm.LINE),
- LINES_LINES_SINGLE("Light Lines - Single Sone", 18, 77, 1, 1, DrawingAlgorithm.LINE),
+ LINES_LINES_SINGLE("Light Lines - Single Sone", 18, 77, 2, 2, DrawingAlgorithm.LINE),
CONTROLLER_CAP("Controller Cap", 19, 11, 0, 1, DrawingAlgorithm.NONE),
POWER_CONNECTOR("Power Connector", 20, 11, 0, 1, DrawingAlgorithm.NONE);
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.nanoleaf.internal.layout.shape;
+
+import java.awt.Graphics2D;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.nanoleaf.internal.layout.DrawingSettings;
+import org.openhab.binding.nanoleaf.internal.layout.PanelState;
+import org.openhab.binding.nanoleaf.internal.layout.Point2D;
+import org.openhab.binding.nanoleaf.internal.layout.ShapeType;
+
+/**
+ * A panel that shouldn't be drawn (power connector, controller, ...).
+ *
+ * Especially lines can have controllers and power connectors etc on top of each other.
+ *
+ * @author Jørgen Austvik - Initial contribution
+ */
+@NonNullByDefault
+public class NoDraw extends Panel {
+
+ private final Point2D position;
+
+ public NoDraw(ShapeType shapeType, int panelId, Point2D position) {
+ super(shapeType);
+ this.position = position;
+ }
+
+ @Override
+ public List<Point2D> generateOutline() {
+ return Arrays.asList(position);
+ }
+
+ @Override
+ public void draw(Graphics2D graphics, DrawingSettings settings, PanelState state) {
+ }
+}
Point2D pos3 = new Point2D(hexShape.getPosX(), hexShape.getPosY());
return new Hexagon(shapeType, hexShape.getPanelId(), pos3, hexShape.getOrientation());
+ case LINE:
+ return new SingleLine(shapeType, positionDatum);
+
case CORNER:
return new HexagonCorners(shapeType, positionDatum);
+ case NONE:
+ PositionDatum noShape = positionDatum.get(0);
+ Point2D pos4 = new Point2D(noShape.getPosX(), noShape.getPosY());
+ return new NoDraw(shapeType, noShape.getPanelId(), pos4);
+
default:
PositionDatum shape = positionDatum.get(0);
- Point2D pos4 = new Point2D(shape.getPosX(), shape.getPosY());
- return new Point(shapeType, shape.getPanelId(), pos4);
+ Point2D pos5 = new Point2D(shape.getPosX(), shape.getPosY());
+ return new Point(shapeType, shape.getPanelId(), pos5);
}
}
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.nanoleaf.internal.layout.shape;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.awt.Stroke;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.nanoleaf.internal.layout.DrawingSettings;
+import org.openhab.binding.nanoleaf.internal.layout.ImagePoint2D;
+import org.openhab.binding.nanoleaf.internal.layout.PanelState;
+import org.openhab.binding.nanoleaf.internal.layout.Point2D;
+import org.openhab.binding.nanoleaf.internal.layout.ShapeType;
+import org.openhab.binding.nanoleaf.internal.model.PositionDatum;
+import org.openhab.core.library.types.HSBType;
+
+/**
+ * A single line.
+ *
+ * @author Jørgen Austvik - Initial contribution
+ */
+@NonNullByDefault
+public class SingleLine extends Panel {
+
+ private static final int CORNER_DIAMETER = 4;
+ private static final int LINE_WIDTH = 6;
+
+ private final List<PositionDatum> corners;
+
+ public SingleLine(ShapeType shapeType, List<PositionDatum> corners) {
+ super(shapeType);
+
+ this.corners = Collections.unmodifiableList(new ArrayList<>(corners));
+ }
+
+ @Override
+ public List<Point2D> generateOutline() {
+ List<Point2D> result = new ArrayList<>(corners.size());
+ for (PositionDatum corner : corners) {
+ result.add(new Point2D(corner.getPosX(), corner.getPosY()));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void draw(Graphics2D graphics, DrawingSettings settings, PanelState state) {
+ List<ImagePoint2D> outline = settings.generateImagePoints(generateOutline());
+ Polygon p = new Polygon();
+ for (int i = 0; i < outline.size(); i++) {
+ ImagePoint2D pos = outline.get(i);
+ p.addPoint(pos.getX(), pos.getY());
+ }
+
+ if (settings.shouldFillWithColor()) {
+ Color corner1Color = getColor(corners.get(0).getPanelId(), state);
+ Color corner2Color = getColor(corners.get(0).getPanelId(), state);
+
+ ImagePoint2D center = findCenter(outline);
+
+ Stroke oldStroke = graphics.getStroke();
+ Stroke lineStroke = new BasicStroke(LINE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
+ graphics.setStroke(lineStroke);
+ graphics.setColor(corner1Color);
+ graphics.drawLine(outline.get(0).getX(), outline.get(0).getY(), center.getX(), center.getY());
+ graphics.setColor(corner2Color);
+ graphics.drawLine(outline.get(1).getX(), outline.get(1).getY(), center.getX(), center.getY());
+ graphics.setStroke(oldStroke);
+ }
+
+ if (settings.shouldDrawOutline()) {
+ graphics.setColor(settings.getOutlineColor());
+ graphics.drawPolygon(p);
+ }
+
+ if (settings.shouldDrawCorners()) {
+ for (PositionDatum corner : corners) {
+ ImagePoint2D position = settings.generateImagePoint(new Point2D(corner.getPosX(), corner.getPosY()));
+ graphics.setColor(getColor(corner.getPanelId(), state));
+ graphics.fillOval(position.getX() - CORNER_DIAMETER / 2, position.getY() - CORNER_DIAMETER / 2,
+ CORNER_DIAMETER, CORNER_DIAMETER);
+
+ if (settings.shouldDrawOutline()) {
+ graphics.setColor(settings.getOutlineColor());
+ graphics.drawOval(position.getX() - CORNER_DIAMETER / 2, position.getY() - CORNER_DIAMETER / 2,
+ CORNER_DIAMETER, CORNER_DIAMETER);
+ }
+ }
+ }
+
+ if (settings.shouldDrawLabels()) {
+ graphics.setColor(settings.getLabelColor());
+
+ for (PositionDatum corner : corners) {
+ ImagePoint2D position = settings.generateImagePoint(new Point2D(corner.getPosX(), corner.getPosY()));
+ graphics.drawString(Integer.toString(corner.getPanelId()), position.getX(), position.getY());
+ }
+ }
+ }
+
+ private ImagePoint2D findCenter(List<ImagePoint2D> outline) {
+ Point2D[] bounds = findBounds(outline);
+ int midX = bounds[0].getX() + (bounds[1].getX() - bounds[0].getX()) / 2;
+ int midY = bounds[0].getY() + (bounds[1].getY() - bounds[0].getY()) / 2;
+ return new ImagePoint2D(midX, midY);
+ }
+
+ private static Color getColor(int panelId, PanelState state) {
+ HSBType color = state.getHSBForPanel(panelId);
+ return new Color(color.getRGB());
+ }
+}
static @Nullable Path temporaryDirectory;
@ParameterizedTest
- @ValueSource(strings = { "lasvegas.json", "theduck.json", "squares.json", "wings.json", "spaceinvader.json" })
+ @ValueSource(strings = { "lasvegas.json", "theduck.json", "squares.json", "wings.json", "spaceinvader.json",
+ "lines.json" })
public void testFile(String fileName) throws Exception {
Path file = Path.of("src/test/resources/", fileName);
assertTrue(Files.exists(file), "File should exist: " + file);
--- /dev/null
+{"name":"Lines","serialNo":"S1234567","manufacturer":"Nanoleaf","firmwareVersion":"7.1.0","hardwareVersion":"0.0.4-0","model":"NL59","discovery":null,"effects":{"effectsList":["Beat Drop","Cotton Candy","Cozy Blaze","Fiesta","Good Vibes","Hip Hop","Jalapeno Heat","Kaleidoscope","Mountain Air","Mystic Night","Neon Dreams","Northern Lights","Pop Rocks","Prism","Radioactive","Raspberry Lemonade","Sakura Blossom","Starry Sky","Tuscany Sunset","Waterfall"],"select":"Raspberry Lemonade"},"firmwareUpgrade":null,"panelLayout":{"globalOrientation":{"value":88,"max":360,"min":0},"layout":{"numPanels":13,"sideLength":154,"positionData":[{"panelId":15376,"x":227,"y":337,"o":0,"shapeType":20},{"panelId":12105,"x":227,"y":212,"o":0,"shapeType":18},{"panelId":56841,"x":227,"y":289,"o":0,"shapeType":18},{"panelId":24127,"x":227,"y":164,"o":120,"shapeType":16},{"panelId":55776,"x":119,"y":274,"o":300,"shapeType":18},{"panelId":10400,"x":185,"y":313,"o":300,"shapeType":18},{"panelId":54069,"x":77,"y":250,"o":180,"shapeType":16},{"panelId":6876,"x":119,"y":226,"o":240,"shapeType":18},{"panelId":35357,"x":185,"y":188,"o":240,"shapeType":18},{"panelId":45376,"x":77,"y":125,"o":0,"shapeType":18},{"panelId":16384,"x":77,"y":202,"o":0,"shapeType":18},{"panelId":49828,"x":77,"y":77,"o":300,"shapeType":16},{"panelId":5591,"x":227,"y":337,"o":0,"shapeType":19}]}},"qkihnokomhartlnp":{},"schedules":{},"state":{"brightness":{"value":63,"max":100,"min":0},"colorMode":"effect","ct":{"value":4000,"max":6500,"min":1200},"hue":{"value":0,"max":360,"min":0},"on":{"value":true},"sat":{"value":0,"max":100,"min":0}}}