added a simple rotary encoder

This commit is contained in:
hneemann 2017-04-05 21:21:25 +02:00
parent 65f14ca0db
commit 6dea9d9f2d
11 changed files with 261 additions and 12 deletions

View File

@ -0,0 +1,51 @@
package de.neemann.digital.core.io;
import de.neemann.digital.core.*;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.lang.Lang;
/**
* The Button
*
* @author hneemann
*/
public class RotEncoder implements Element {
/**
* The Button description
*/
public static final ElementTypeDescription DESCRIPTION = new ElementTypeDescription(RotEncoder.class)
.addAttribute(Keys.ROTATE)
.addAttribute(Keys.LABEL);
private final ObservableValue outA;
private final ObservableValue outB;
/**
* Creates a new instance
*
* @param attributes the attributes
*/
public RotEncoder(ElementAttributes attributes) {
outA= new ObservableValue("A", 1).setPinDescription(DESCRIPTION);
outB= new ObservableValue("B", 1).setPinDescription(DESCRIPTION);
}
@Override
public void setInputs(ObservableValues inputs) throws NodeException {
throw new NodeException(Lang.get("err_noInputsAvailable"));
}
@Override
public ObservableValues getOutputs() {
return new ObservableValues(outA, outB);
}
@Override
public void registerNodes(Model model) {
}
}

View File

@ -297,11 +297,12 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
* Is called if this element is clicked with the mouse.
* The call is delegated to the {@link Interactor} of the {@link Shape}
*
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @param posInComponent position in CircuitComponent
* @return true if model is changed
*/
public boolean elementClicked(CircuitComponent cc, Point pos, Sync modelSync) {
public boolean elementClicked(CircuitComponent cc, Point pos, Vector posInComponent, Sync modelSync) {
if (interactor != null)
return interactor.clicked(cc, pos, ioState, element, modelSync);
else
@ -312,11 +313,12 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
* Is called if this element is clicked with the mouse.
* The call is delegated to the {@link Interactor} of the {@link Shape}
*
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @param posInComponent position in CircuitComponent
* @return true if model is changed
*/
public boolean elementPressed(CircuitComponent cc, Point pos, Sync modelSync) {
public boolean elementPressed(CircuitComponent cc, Point pos, Vector posInComponent, Sync modelSync) {
if (interactor != null)
return interactor.pressed(cc, pos, ioState, element, modelSync);
else
@ -327,17 +329,35 @@ public class VisualElement implements Drawable, Moveable, AttributeListener {
* Is called if this element is clicked with the mouse.
* The call is delegated to the {@link Interactor} of the {@link Shape}
*
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @param posInComponent position in CircuitComponent
* @return true if model is changed
*/
public boolean elementReleased(CircuitComponent cc, Point pos, Sync modelSync) {
public boolean elementReleased(CircuitComponent cc, Point pos, Vector posInComponent, Sync modelSync) {
if (interactor != null)
return interactor.released(cc, pos, ioState, element, modelSync);
else
return false;
}
/**
* Is called if the mouse is dragged on this element.
* The call is delegated to the {@link Interactor} of the {@link Shape}
*
* @param cc the calling {@link CircuitComponent}
* @param pos the position
* @param posInComponent position in CircuitComponent
* @return true if model is changed
*/
public boolean elementDragged(CircuitComponent cc, Point pos, Vector posInComponent, Sync modelSync) {
if (interactor != null)
return interactor.dragged(cc, posInComponent, transform, ioState, element, modelSync);
else
return false;
}
@Override
public String toString() {
String lab = elementAttributes.getCleanLabel();

View File

@ -87,6 +87,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
node.add(Probe.DESCRIPTION);
node.add(Out.SEVENDESCRIPTION);
node.add(Out.SEVENHEXDESCRIPTION);
node.add(RotEncoder.DESCRIPTION);
node.add(DummyElement.DATADESCRIPTION);
node.add(DummyElement.TEXTDESCRIPTION);
node.add(Keyboard.DESCRIPTION);

View File

@ -74,6 +74,11 @@ public class ButtonShape implements Shape {
});
return true;
}
@Override
public boolean dragged(CircuitComponent cc, Vector pos, Transform trans, IOState ioState, Element element, Sync modelSync) {
return false;
}
};
}

View File

@ -2,6 +2,8 @@ package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.graphics.Transform;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.sync.Sync;
@ -26,4 +28,9 @@ public abstract class Interactor implements InteractorInterface {
public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
return false;
}
@Override
public boolean dragged(CircuitComponent cc, Vector pos, Transform transform, IOState ioState, Element element, Sync modelSync) {
return false;
}
}

View File

@ -2,6 +2,8 @@ package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.graphics.Transform;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.sync.Sync;
@ -45,4 +47,16 @@ public interface InteractorInterface {
* @return true if model is changed
*/
boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync);
/**
* Called mouse is dragged on running model
*
* @param cc the CircuitComponent
* @param pos the position in the containing component
* @param transform transformation to transform shape coordinates to the containing component
* @param ioState the state of the element
* @return true if model is changed
*/
boolean dragged(CircuitComponent cc, Vector pos, Transform transform, IOState ioState, Element element, Sync modelSync);
}

View File

@ -0,0 +1,130 @@
package de.neemann.digital.draw.shapes;
import de.neemann.digital.core.Observer;
import de.neemann.digital.core.element.Element;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.PinDescriptions;
import de.neemann.digital.draw.elements.IOState;
import de.neemann.digital.draw.elements.Pin;
import de.neemann.digital.draw.elements.Pins;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.graphics.Polygon;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.sync.Sync;
import java.awt.*;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE;
import static de.neemann.digital.draw.shapes.GenericShape.SIZE2;
/**
* The rotary encoder shape
*
* @author hneemann
*/
public class RotEncoderShape implements Shape {
private final String label;
private final PinDescriptions outputs;
private int state;
/**
* Creates a new instance
*
* @param attr the attributes
* @param inputs the inputs
* @param outputs the outputs
*/
public RotEncoderShape(ElementAttributes attr, PinDescriptions inputs, PinDescriptions outputs) {
this.outputs = outputs;
this.label = attr.getLabel();
}
@Override
public Pins getPins() {
return new Pins()
.add(new Pin(new Vector(SIZE * 3, 0), outputs.get(0)))
.add(new Pin(new Vector(SIZE * 3, SIZE), outputs.get(1)));
}
@Override
public InteractorInterface applyStateMonitor(IOState ioState, Observer guiObserver) {
ioState.getOutput(0).addObserverToValue(guiObserver);
return new InteractorInterface() {
private int initialState;
private boolean initial;
@Override
public boolean clicked(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
return false;
}
@Override
public boolean pressed(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
initial = true;
return false;
}
@Override
public boolean released(CircuitComponent cc, Point pos, IOState ioState, Element element, Sync modelSync) {
return false;
}
@Override
public boolean dragged(CircuitComponent cc, Vector pos, Transform trans, IOState ioState, Element element, Sync modelSync) {
if (ioState != null) {
Vector p = pos.sub(trans.transform(new Vector(SIZE2, SIZE2)));
final int dist = p.x * p.x + p.y * p.y;
if (dist > 100 && dist < 900) {
int s = (int) (Math.atan2(p.y, p.x) / Math.PI * 16);
if (initial) {
initialState = s;
initial = false;
} else {
// somewhat unusual but ensures that every step is visible to the model.
int ds = 0;
if (s > initialState) ds = 1;
else if (s < initialState) ds = -1;
initialState = s;
if (ds != 0) {
state += ds;
modelSync.access(() -> {
boolean a = ((state / 2) & 1) != 0;
boolean b = (((state + 1) / 2) & 1) != 0;
ioState.getOutput(0).setBool(a);
ioState.getOutput(1).setBool(b);
});
return true;
}
}
} else
initial = true;
}
return false;
}
};
}
@Override
public void drawTo(Graphic graphic, boolean heighLight) {
graphic.drawPolygon(new Polygon(true)
.add(SIZE * 3, -SIZE)
.add(SIZE * 3, SIZE * 2)
.add(-SIZE, SIZE * 2)
.add(-SIZE, -SIZE), Style.NORMAL);
graphic.drawCircle(new Vector(-SIZE, -SIZE), new Vector(SIZE * 2, SIZE * 2), Style.NORMAL);
graphic.drawCircle(new Vector(-SIZE2, -SIZE2), new Vector(SIZE + SIZE2, SIZE + SIZE2), Style.THIN);
final double alpha = state / 16.0 * Math.PI;
int x = (int) ((SIZE + 1) * Math.cos(alpha));
int y = (int) ((SIZE + 1) * Math.sin(alpha));
graphic.drawLine(new Vector(SIZE2, SIZE2), new Vector(SIZE2 + x, SIZE2 + y), Style.NORMAL);
Vector textPos = new Vector(SIZE, SIZE * 2 + 4);
graphic.drawText(textPos, textPos.add(1, 0), label, Orientation.CENTERTOP, Style.NORMAL);
}
}

View File

@ -97,6 +97,7 @@ public final class ShapeFactory {
map.put(Out.SEVENDESCRIPTION.getName(), SevenSegShape::new);
map.put(Out.SEVENHEXDESCRIPTION.getName(), SevenSegHexShape::new);
map.put(DummyElement.DATADESCRIPTION.getName(), DataShape::new);
map.put(RotEncoder.DESCRIPTION.getName(), RotEncoderShape::new);
map.put(Break.DESCRIPTION.getName(), BreakShape::new);
map.put(Delay.DESCRIPTION.getName(), (attributes, inputs, outputs) -> new DelayShape());

View File

@ -1287,11 +1287,13 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
private interface Actor {
boolean interact(CircuitComponent cc, Point p, Sync modelSync);
boolean interact(CircuitComponent cc, Point p, Vector posInComponent, Sync modelSync);
}
private final class MouseControllerRun extends MouseController {
private boolean dragHandled;
private MouseControllerRun(Cursor cursor) {
super(cursor);
}
@ -1299,8 +1301,11 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
@Override
void pressed(MouseEvent e) {
VisualElement ve = getInteractableElementAt(e);
if (ve != null)
if (ve != null) {
interact(e, ve::elementPressed);
dragHandled = true;
} else
dragHandled = false;
}
private VisualElement getInteractableElementAt(MouseEvent e) {
@ -1326,11 +1331,18 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
interact(e, ve::elementClicked);
}
@Override
boolean dragged(MouseEvent e) {
VisualElement ve = getInteractableElementAt(e);
if (ve != null)
interact(e, ve::elementDragged);
return dragHandled;
}
private void interact(MouseEvent e, Actor actor) {
Point p = new Point(e.getX(), e.getY());
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
boolean modelHasChanged = actor.interact(CircuitComponent.this, p, modelSync);
boolean modelHasChanged = actor.interact(CircuitComponent.this, p, getPosVector(e), modelSync);
if (modelHasChanged) {
if (manualChangeObserver != null)
manualChangeObserver.hasChanged();

View File

@ -372,6 +372,10 @@ Die gesammte Speichergröße beträgt damit damit dx*dy*2 Speicherworte.</string
<string name="elem_PFET_pin_G">Gate</string>
<string name="elem_PFET_pin_out1">Source</string>
<string name="elem_PFET_pin_out2">Drain</string>
<string name="elem_RotEncoder">Drehencoder</string>
<string name="elem_RotEncoder_tt">Drehencoder zur zustandsfreien Erfassung von Drehbewegungen.</string>
<string name="elem_RotEncoder_pin_A">Encodersignal A</string>
<string name="elem_RotEncoder_pin_B">Encodersignal B</string>
<string name="error">Fehler</string>
<string name="err_DFlipflopWithoutALabel">Flipflop hat keine Bezeichnung!</string>

View File

@ -359,6 +359,10 @@
<string name="elem_PFET_pin_G">Gate</string>
<string name="elem_PFET_pin_out1">Source</string>
<string name="elem_PFET_pin_out2">Drain</string>
<string name="elem_RotEncoder">Rotary Encoder</string>
<string name="elem_RotEncoder_tt">Rotary encoder for stateless detecting rotational movements.</string>
<string name="elem_RotEncoder_pin_A">encoder signal A</string>
<string name="elem_RotEncoder_pin_B">encoder signal B</string>
<string name="error">Error</string>
<string name="err_DFlipflopWithoutALabel">D-flip-flop has no label set</string>