Skip to content

Commit

Permalink
Add support for import kicad position files (#2237)
Browse files Browse the repository at this point in the history
  • Loading branch information
breiler authored Jun 20, 2023
1 parent 9171f2b commit d2a6e05
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.ugs.nbp.designer.io.c2d.C2dReader;
import com.willwinder.ugs.nbp.designer.io.dxf.DxfReader;
import com.willwinder.ugs.nbp.designer.io.eagle.EaglePnpReader;
import com.willwinder.ugs.nbp.designer.io.kicad.KiCadPosReader;
import com.willwinder.ugs.nbp.designer.io.svg.SvgReader;
import com.willwinder.ugs.nbp.designer.logic.Controller;
import com.willwinder.ugs.nbp.designer.logic.ControllerFactory;
Expand Down Expand Up @@ -74,6 +75,7 @@ public void actionPerformed(ActionEvent e) {
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Autodesk CAD (.dxf)", "dxf"));
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Carbide Create (.c2d)", "c2d"));
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Eagle (.mnt, .mnb)", "mnt", "mnb"));
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("KiCad (.pos)", "pos"));
fileDialog.showOpenDialog(null);

BackendAPI backend = CentralLookup.getDefault().lookup(BackendAPI.class);
Expand All @@ -92,9 +94,13 @@ public void actionPerformed(ActionEvent e) {
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".c2d")) {
C2dReader reader = new C2dReader();
optionalDesign = reader.read(f);
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".mnt") || StringUtils.endsWithIgnoreCase(f.getName(), ".mnb")) {
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".mnt") ||
StringUtils.endsWithIgnoreCase(f.getName(), ".mnb")) {
EaglePnpReader reader = new EaglePnpReader();
optionalDesign = reader.read(f);
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".pos")) {
KiCadPosReader reader = new KiCadPosReader();
optionalDesign = reader.read(f);
}

if (optionalDesign.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public WorldOfScifiSource() {
cliparts.add(new FontClipart("Alien", Category.PEOPLE_AND_CHARACTERS, font, "n", this));
cliparts.add(new FontClipart("TRON light cycle", Category.TRANSPORTATION, font, "o", this));
cliparts.add(new FontClipart("Star Trek - Phaser", Category.TOOLS, font, "p", this));
cliparts.add(new FontClipart("Alien vs Predator", Category.SIGNS_AND_SYMBOLS, font, "q", this));
cliparts.add(new FontClipart("Alien vs Predator", Category.SIGNS_AND_SYMBOLS, font.deriveFont(font.getSize() * 0.5f), "q", this));
cliparts.add(new FontClipart("Robinson Robot.", Category.PEOPLE_AND_CHARACTERS, font, "r", this));
cliparts.add(new FontClipart("Star Wars", Category.SIGNS_AND_SYMBOLS, font, "s", this));
cliparts.add(new FontClipart("Star Trek", Category.SIGNS_AND_SYMBOLS, font, "t", this));
Expand Down Expand Up @@ -92,7 +92,7 @@ public WorldOfScifiSource() {
cliparts.add(new FontClipart("Dave", Category.PEOPLE_AND_CHARACTERS, font, "1", this));
cliparts.add(new FontClipart("Star Wars - R2D2", Category.PEOPLE_AND_CHARACTERS, font, "2", this));
cliparts.add(new FontClipart("Arachnid", Category.PEOPLE_AND_CHARACTERS, font, "3", this));
cliparts.add(new FontClipart("Independence day", Category.SIGNS_AND_SYMBOLS, font, "4", this));
cliparts.add(new FontClipart("Independence day", Category.SIGNS_AND_SYMBOLS, font.deriveFont(font.getSize() * 0.5f), "4", this));
cliparts.add(new FontClipart("Babylon 5", Category.SIGNS_AND_SYMBOLS, font, "5", this));
cliparts.add(new FontClipart("Buck Rogers - Twiki", Category.PEOPLE_AND_CHARACTERS, font, "6", this));
cliparts.add(new FontClipart("Space man", Category.PEOPLE_AND_CHARACTERS, font, "8", this));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.willwinder.ugs.nbp.designer.io.kicad;

import com.willwinder.ugs.nbp.designer.entities.Anchor;
import com.willwinder.ugs.nbp.designer.entities.Entity;
import com.willwinder.ugs.nbp.designer.entities.cuttable.Point;
import com.willwinder.ugs.nbp.designer.io.DesignReader;
import com.willwinder.ugs.nbp.designer.io.DesignReaderException;
import com.willwinder.ugs.nbp.designer.model.Design;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.awt.geom.Point2D;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.willwinder.ugs.nbp.designer.Utils.parseDouble;

public class KiCadPosReader implements DesignReader {
@Override
public Optional<Design> read(File file) {
try {
return read(new FileInputStream(file));
} catch (FileNotFoundException e) {
throw new DesignReaderException("Could not read file", e);
}
}

@Override
public Optional<Design> read(InputStream resourceAsStream) {
try {
String fileData = IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
Pattern pattern = Pattern.compile("\\R");

List<Entity> points = pattern.splitAsStream(fileData)
.filter(line -> !line.startsWith("#"))
.map(this::parsePoint)
.collect(Collectors.toList());

Design design = new Design();
design.setEntities(points);
return Optional.of(design);
} catch (IOException e) {
throw new DesignReaderException("Could not parse file", e);
}
}

private Point parsePoint(String line) {
line = line.replaceAll(" {2}", " ");
String[] columns = StringUtils.split(line, " ");

String name = columns[0] + " - " + columns[1] + " " + columns[2];

Point point = new Point();
point.setName(name);
point.setPosition(Anchor.CENTER, new Point2D.Double(parseDouble(columns[3]), parseDouble(columns[4])));
point.setRotation(parseDouble(columns[5]));
return point;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.willwinder.ugs.nbp.designer.io.eagle;

import com.willwinder.ugs.nbp.designer.entities.Entity;
import com.willwinder.ugs.nbp.designer.model.Design;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import java.awt.geom.Point2D;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class EaglePnpReaderTest {

@Test
public void readValidFile() {
EaglePnpReader reader = new EaglePnpReader();
Optional<Design> designOptional = reader.read(IOUtils.toInputStream("C1 26.23 2.92 225 0.47uF C0402\n" +
"C2 25.44 3.57 45 0.1uF C0402", StandardCharsets.UTF_8));

assertTrue(designOptional.isPresent());

Design design = designOptional.get();
List<Entity> entities = design.getEntities();
assertEquals(2, entities.size());

assertEquals("C1 - 0.47uF C0402", entities.get(0).getName());
assertEquals(26.23, entities.get(0).getCenter().getX(), 0.001);
assertEquals(2.92, entities.get(0).getCenter().getY(), 0.001);
assertEquals(225, entities.get(0).getRotation(), 0.001);

assertEquals("C2 - 0.1uF C0402", entities.get(1).getName());
assertEquals(25.44, entities.get(1).getCenter().getX(), 0.001);
assertEquals(3.57, entities.get(1).getCenter().getY(), 0.001);
assertEquals(45, entities.get(1).getRotation(), 0.001);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.willwinder.ugs.nbp.designer.io.kicad;

import com.willwinder.ugs.nbp.designer.entities.Entity;
import com.willwinder.ugs.nbp.designer.model.Design;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class KiCadPosReaderTest {
@Test
public void readValidFile() {
KiCadPosReader reader = new KiCadPosReader();
Optional<Design> designOptional = reader.read(IOUtils.toInputStream("### Footprint positions - created on 2023 June 20, Tuesday 14:16:48 ###\n" +
"### Printed by KiCad version 7.0.5-0\n" +
"## Unit = mm, Angle = deg.\n" +
"## Side : top\n" +
"# Ref Val Package PosX PosY Rot Side\n" +
"C1 10uF CP_Radial_D10.0mm_P5.00mm 141.6050 -99.6950 90.0000 top\n" +
"C2 680nF C_Disc_D4.7mm_W2.5mm_P5.00mm 137.1600 -125.0950 90.0000 top\n" +
"P1 IN Altech_AK300_1x02_P5.00mm_45-Degree 166.3700 -105.4100 0.0000 top", StandardCharsets.UTF_8));

assertTrue(designOptional.isPresent());

Design design = designOptional.get();
List<Entity> entities = design.getEntities();
assertEquals(3, entities.size());

assertEquals("C1 - 10uF CP_Radial_D10.0mm_P5.00mm", entities.get(0).getName());
assertEquals(141.6050, entities.get(0).getCenter().getX(), 0.001);
assertEquals(-99.6950, entities.get(0).getCenter().getY(), 0.001);
assertEquals(90, entities.get(0).getRotation(), 0.001);

assertEquals("C2 - 680nF C_Disc_D4.7mm_W2.5mm_P5.00mm", entities.get(1).getName());
assertEquals(137.1600, entities.get(1).getCenter().getX(), 0.001);
assertEquals(-125.0950, entities.get(1).getCenter().getY(), 0.001);
assertEquals(90, entities.get(1).getRotation(), 0.001);

assertEquals("P1 - IN Altech_AK300_1x02_P5.00mm_45-Degree", entities.get(2).getName());
assertEquals(166.3700, entities.get(2).getCenter().getX(), 0.001);
assertEquals(-105.4100, entities.get(2).getCenter().getY(), 0.001);
assertEquals(0, entities.get(2).getRotation(), 0.001);
}
}

0 comments on commit d2a6e05

Please sign in to comment.