mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-28 07:28:20 -04:00
added a simple rotary encoder
This commit is contained in:
parent
65f14ca0db
commit
6dea9d9f2d
51
src/main/java/de/neemann/digital/core/io/RotEncoder.java
Normal file
51
src/main/java/de/neemann/digital/core/io/RotEncoder.java
Normal 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) {
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user