mipix

package module
v0.0.0-...-5b3fe6d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2025 License: MIT Imports: 13 Imported by: 0

README

mipix

Go Reference

A package to assist the development of Ebitengine pixel art games.

This package allows you to implement your game working with logical canvases and ignoring Game.Layout() completely. Scaling is managed internally with pixel art aware scaling algorithms, and support for camera movement, zoom and screen shakes are also available through the API.

Features

  • Draw pixel art logically without having to deal with device scale factors, DPI, scaling and projections yourself.
  • Basic camera with smooth tracking, zoom and screen shakes. Most behaviors can be customized through interfaces.
  • Interleave high resolution and logical draws when necessary.

How to use mipix

The best way to learn mipix is to read the introductory tutorial with code samples and the package documentation. More code samples are available on tinne26/mipix-examples, but you should really spend a couple minutes to understand how the draw model works on mipix first.

If you are too lazy for that, at least read the summary:

  • The canvas you receive on Draw() represents pixels 1-to-1. Your pixel art must be drawn directly to the canvas, in its original size. Just draw your pixel art.
  • The canvas you receive on Draw() does not always have the same size, and it does not necessarily match the resolution you set for your game with mipix.SetResolution(W, H). This can happen due to zoom effects, smooth camera movement and others. Just focus on rendering the logical area requested by mipix.Camera().Area().

Context

This package implements the second model described on lopix. If lopix implements the simplest model for pixel art games, mipix is slightly more advanced and provides a much more practical foundation to build pixel art games:

  • You are expected to place most of your game world elements at integer coordinates.
  • Your draw method receives a logically sized offscreen corresponding to a specific area of your game world. This area can vary based on the current camera position, zoom and shake, but you are simply given the task of rendering a logical area of your game world, directly and in pure pixels, to a logically sized canvas.
  • For UI and other camera-independent parts of your game, you can create mipix.Offscreens manually, again with pure pixel based sizes, render to them and then use the built-in Offscreen.Project() method to deal with scaling and stretching and filtering and all that nonsense.

While side scrollers can be implemented with this model, that's probably not ideal. In most side-scrollers, developers use floating point positions for characters, which are smoothly "sliding through the floor" as animations change[^1]. Doing this requires drawing the characters to a high resolution canvas. The API offers basic support for this, but it's not the main focus of the package. If you have many fractionally positioned characters and elements, the mipix model might not be the best match for your game.

[^1]: As opposed to this "sliding animation" model, pixel artists can design their animations to look good as they advance through the concrete pixel grid, with concrete pixel advances between frames. This isn't supported by most animation libraries, takes more work for the artist, takes more work for the developer, and makes animation usage more rigid (e.g., need separate "walking on stairs" animation)... but with this model, you can draw moving characters in the logical canvas. This is more common on RPGs and top down games than side scrollers, but it's fairly uncommon everywhere in general. Smoothness perception throughout movement is also of a different class.

Random thoughts and comments

  • Need to add camera area limits, blockers and so on.
  • Motion blur should be considered for the high resolution draws and maybe even the main canvas. While most people say they hate motion blur, that mostly refers to motion blur as an aesthetic effect. Light amounts of motion blur could greatly reduce image stability problems during movement.
  • The pixel art sampling shaders do cause a visible amount of "fattening" for the pixels.

Documentation

Index

Constants

View Source
const Ctrl = ebiten.KeyControl

Quick alias to the control key for use with AccessorDebug.Printfk().

View Source
const Pi = 3.141592653589793

Variables

This section is empty.

Functions

func GetResolution

func GetResolution() (width, height int)

Returns the game's base resolution. See SetResolution() for more details.

func LayoutHasChanged

func LayoutHasChanged() bool

Returns whether a layout change has happened on the current tick. Layout changes happen whenever the game window is resized in windowed mode, the game switches between windowed and fullscreen modes, or the device scale factor changes (possibly due to a monitor change).

This function is only relevant if you need to redraw game borders manually and efficiently or resize offscreens. Even if you have AccessorRedraw.SetManaged(true), you rarely need to worry about the state of the layout; any changes will automatically trigger a redraw request.

func QueueDraw

func QueueDraw(handler func(logicalCanvas *ebiten.Image))

Ignore this function unless you are already using QueueHiResDraw(). This function is only relevant when trying to interleave logical and high resolution draws.

The canvas passed to the callback will be preemptively cleared if the previous draw was a high resolution draw.

Must only be called from Game.Draw() or successive draw callbacks.

func QueueHiResDraw

func QueueHiResDraw(handler func(viewport, hiResCanvas *ebiten.Image))

Schedules the given handler to be invoked after the current drawing function and any other queued draws finish.

The viewport passed to the handler is the full game screen canvas, including any possibly unused borders, while hiResCanvas is a subimage corresponding to the active area of the viewport.

Using this function is necessary if you want to render high resolution graphics. This includes vectorial text, some UI, smoothly moving entities, shader effects and more.

Must only be called from Game.Draw() or successive draw callbacks. See also QueueDraw().

func Run

func Run(game Game) error

Equivalent to ebiten.RunGame(), but expecting a ebipixel Game instead of an ebiten.Game.

Will panic if invoked before SetResolution().

func SetResolution

func SetResolution(width, height int)

Sets the game's base resolution. This defines the game's aspect ratio and logical canvas size at integer coordinates and zoom = 1.0.

Types

type AccessorCamera

type AccessorCamera struct{}

See Camera().

func Camera

func Camera() AccessorCamera

Provides access to camera-related functionality in a structured manner. Use through method chaining, e.g.:

mipix.Camera().Zoom(2.0)

func (AccessorCamera) Area

Returns the logical area of the game that has to be rendered on Game.Draw()'s canvas or successive logical draws. Notice that this can change after each Game.Update(), since the camera might be zoomed or shaking.

Notice that the area will typically be slightly different between Game.Update() and Game.Draw(). If you need more manual control over that, see AccessorCamera.FlushCoordinates().

func (AccessorCamera) AreaF64

func (AccessorCamera) AreaF64() (minX, minY, maxX, maxY float64)

Similar to AccessorCamera.Area(), but without rounding up the coordinates and returning the exact values. Rarely necessary in practice.

func (AccessorCamera) EndShake

func (AccessorCamera) EndShake(fadeOut TicksDuration, channels ...shaker.Channel)

Stops a screen shake. This can be used to stop shakes initiated with AccessorCamera.StartShake(), but also to stop triggered shakes early or ensure that no shakes remain active after screen transitions and similar. Calling this method repeatedly to make sure that shaking is either stopped or stopping is possible and safe.

If no shaker channel(s) are specified, the end shake command will be sent to the default channel zero.

func (AccessorCamera) FlushCoordinates

func (AccessorCamera) FlushCoordinates()

This method allows updating the AccessorCamera.Area() even during Game.Update(). By default, this happens automatically after Game.Update(), but flushing the coordinates can force an earlier update.

Notice that only one camera update can happen per tick, so the automatic camera update will be skipped if you flush coordinates manually during Game.Update(). Calling this method multiple times during the same update will only update coordinates on the first invocation.

If you don't need this feature, it's better to forget about this method. This is only necessary if you need the camera area to remain perfectly consistent between update and draw(s), in which case you update the player position first, then notify the coordinates and finally flush them.

func (AccessorCamera) GetShaker

func (AccessorCamera) GetShaker(channel ...shaker.Channel) shaker.Shaker

Returns the shaker interface associated to the given shaker channel (or to the default channel zero if none is passed). Passing multiple channels will make the function panic.

See AccessorCamera.SetShaker() for more details.

func (AccessorCamera) GetTracker

func (AccessorCamera) GetTracker() tracker.Tracker

Returns the current tracker. See AccessorCamera.SetTracker() for more details.

func (AccessorCamera) GetZoom

func (AccessorCamera) GetZoom() (current, target float64)

Returns the current and target zoom levels.

func (AccessorCamera) GetZoomer

func (AccessorCamera) GetZoomer() zoomer.Zoomer

Returns the current zoomer.Zoomer interface. See AccessorCamera.SetZoomer() for more details.

func (AccessorCamera) IsShaking

func (AccessorCamera) IsShaking(channel ...shaker.Channel) bool

If no shaker channel is specified, the function returns whether any camera shake is active. If a shaker channel is specified, the function will only return whether that specific channel is active.

func (AccessorCamera) NotifyCoordinates

func (AccessorCamera) NotifyCoordinates(x, y float64)

Feeds the camera the newest target coordinates to point to or look at. The time that it takes to reach these new coordinates will depend on the behavior of the current tracker.Tracker.

You can pass coordinates as many times as you want, the target position is always set to the most recent pair.

func (AccessorCamera) ResetCoordinates

func (AccessorCamera) ResetCoordinates(x, y float64)

Immediately sets the camera coordinates to the given values. Commonly used when changing scenes or maps.

func (AccessorCamera) ResetZoom

func (AccessorCamera) ResetZoom(zoomLevel float64)

func (AccessorCamera) SetShaker

func (AccessorCamera) SetShaker(shaker shaker.Shaker, channel ...shaker.Channel)

Sets a shaker. By default the screen shaker interface is nil, and shakes are handled by a fallback shaker.Random.

If you don't specify any shaker channel, the shaker will be set to the default channel zero. Attempting to pass multiple channels will make the function panic.

func (AccessorCamera) SetTracker

func (AccessorCamera) SetTracker(tracker tracker.Tracker)

Sets the tracker in charge of updating the camera position. By default the tracker is nil, and tracking is handled by a fallback tracker.SpringTailer. If you want something simpler at the start, you can easily switch to an instant tracker:

import "github.com/edwinsyarief/mipix/tracker"
mipix.Camera().SetTracker(tracker.Instant)

func (AccessorCamera) SetZoomer

func (AccessorCamera) SetZoomer(zoomer zoomer.Zoomer)

Sets the zoomer.Zoomer in charge of updating camera zoom levels. By default the zoomer is nil, and zoom levels are handled by a fallback zoomer.Quadratic.

func (AccessorCamera) StartShake

func (AccessorCamera) StartShake(fadeIn TicksDuration, channels ...shaker.Channel)

Starts a screen shake that will continue indefinitely until stopped by AccessorCamera.EndShake(). If no shaker channel(s) are specified, the shake will start on the default channel zero.

Calling this method repeatedly to force a shaker to start if not yet active is possible and safe.

func (AccessorCamera) TriggerShake

func (AccessorCamera) TriggerShake(fadeIn, duration, fadeOut TicksDuration, channels ...shaker.Channel)

Triggers a screenshake with specific fade in, duration and fade out tick durations. If no explicit shaker channels are passed, the trigger will be applied to the default channel zero.

Currently, triggering a shake on a channel that's already active and with an undefined duration will override the shake duration, eventually bringing it to a stop. That being said, this is considered an ambiguous situation that you should try to avoid in the first place.

func (AccessorCamera) Zoom

func (AccessorCamera) Zoom(newZoomLevel float64)

Sets a new target zoom level. The transition from the current zoom level to the new one is managed by a zoomer.Zoomer.

Important: very low zoom levels are extremely dangerous, as they make the camera area grow towards infinity. In fact, ebipixel doesn't expect you to go below 0.05, and stops trying to predict/optimize canvas sizes for zoom transitions beyond that point.

type AccessorConvert

type AccessorConvert struct{}

See Convert().

func Convert

func Convert() AccessorConvert

Provides access to coordinate conversions in a structured manner. Use through method chaining, e.g.:

cx, cy := ebiten.CursorPosition()
lx, ly := mipix.Convert().ToLogicalCoords(cx, cy)

func (AccessorConvert) ToGameResolution

func (AccessorConvert) ToGameResolution(x, y int) (float64, float64)

Transforms coordinates obtained from ebiten.CursorPosition() and similar functions to screen coordinates rescaled between (0, 0) and (GameWidth, GameHeight).

Commonly used to see what is being clicked on the game's UI (when the UI is pure pixel art).

func (AccessorConvert) ToLogicalCoords

func (AccessorConvert) ToLogicalCoords(x, y int) (float64, float64)

Transforms coordinates obtained from ebiten.CursorPosition() and similar functions to coordinates within the game's global logical space.

Commonly used to see what is being clicked on the game's world.

func (AccessorConvert) ToRelativeCoords

func (AccessorConvert) ToRelativeCoords(x, y int) (float64, float64)

Transforms coordinates obtained from ebiten.CursorPosition() and similar functions to relative screen coordinates between 0 and 1.

Commonly used to see what is being clicked on the game's UI or applying fancy shaders and effects that depend on the cursor's relative position on screen.

type AccessorDebug

type AccessorDebug struct{}

See Debug().

func Debug

func Debug() AccessorDebug

Provides access to debugging functionality in a structured manner. Use through method chaining, e.g.:

mipix.Debug().Drawf("current tick: %d", mipix.Tick().Now())

func (AccessorDebug) Drawf

func (AccessorDebug) Drawf(format string, args ...any)

Similar to Printf debugging, but drawing the text on the top left of the screen (instead of printing on the terminal). Multi-line text is not supported; use multiple Drawf commands in sequence instead.

You can call this function at any point, even during Game.Update(). Strings will be queued and rendered at the end of the next draw.

func (AccessorDebug) Printfe

func (AccessorDebug) Printfe(everyNTicks uint64, format string, args ...any)

Similar to fmt.Printf(), but only prints every N ticks. For example, in most games using N = 60 will lead to print once per second.

func (AccessorDebug) Printfk

func (AccessorDebug) Printfk(key ebiten.Key, format string, args ...any)

Similar to fmt.Printf(), but only prints if the given key is pressed. Common keys: ebiten.KeyShiftLeft, ebiten.KeyControl, ebiten.KeyDigit1.

func (AccessorDebug) Printfr

func (AccessorDebug) Printfr(firstTick, lastTick uint64, format string, args ...any)

Similar to fmt.Printf(), but expects two tick counts as the first arguments. The function will only print during the period elapsed between those two tick counts. Some examples:

mipix.Debug().Printfr(0, 0, "only print on the first tick\n")
mipix.Debug().Printfr(180, 300, "print from 3s to 5s lapse\n")

type AccessorHiRes

type AccessorHiRes struct{}

See HiRes().

func HiRes

func HiRes() AccessorHiRes

Provides access to high resolution drawing methods in a structured manner. Use through method chaining, e.g.:

mipix.HiRes().Draw(target, source, x, y)

func (AccessorHiRes) Draw

func (self AccessorHiRes) Draw(target, source *ebiten.Image, transform *ebimath.Transform)

Draws the source into the given target at the given global logical coordinates (camera origin is automatically subtracted).

Notice that ebipixel's main focus is not high resolution drawing, and this method is not expected to be used more than a dozen times per frame. If you are only drawing the main character or a few entities at floating point positions, using this method should be fine. If you are trying to draw every element of your game with this, or relying on this for a particle system, you are misusing mipix.

Many more high resolution drawing features could be provided, and some might be added in the future, but this is not the main goal of the project.

All that being said, this is not a recommendation to avoid this method. This method is perfectly functional and a very practical tool in many scenarios.

func (AccessorHiRes) FillOverRect

func (self AccessorHiRes) FillOverRect(target *ebiten.Image, minX, minY, maxX, maxY float64, fillColor color.Color)

Fills the logical area designated by the given coordinates with fillColor. If you need fills with alpha blending directly without high resolution, see the utils subpackage.

func (AccessorHiRes) Height

func (self AccessorHiRes) Height() int

func (AccessorHiRes) Width

func (self AccessorHiRes) Width() int

type AccessorRedraw

type AccessorRedraw struct{}

See Redraw().

func Redraw

func Redraw() AccessorRedraw

Provides access to methods for efficient GPU usage in a structured manner. Use through method chaining, e.g.:

mipix.Redraw().SetManaged(true)

In some games and applications it's possible to spare GPU by using ebiten.SetScreenClearedEveryFrame(false) and omitting redundant draw calls.

The redraw accessor allows you to synchronize this process with ebipixel itself, as there are some projections that would otherwise fall outside your control.

By default, redraws are executed on every frame. If you want to manage them more efficiently, you can do the following:

func (AccessorRedraw) IsManaged

func (AccessorRedraw) IsManaged() bool

Returns whether manual redraw management is enabled or not.

func (AccessorRedraw) Pending

func (AccessorRedraw) Pending() bool

Returns whether a redraw is still pending. Notice that besides explicit requests, a redraw can also be pending due to a canvas resize, the modification of the scaling properties or others.

This method is often used like this:

func (game *Game) Draw(canvas *ebiten.Image) {
    if !mipix.Redraw().Pending() { return }
    // ...
}

func (AccessorRedraw) Request

func (AccessorRedraw) Request()

Notifies ebipixel that the next Game.Draw() needs to be projected to the screen. Requests are typically issued when relevant input or events are detected during Game.Update().

Zoom and camera changes are also auto-detected.

This function can be called multiple times within a single update, it's only doing the equivalent of "needs redraw = true".

func (AccessorRedraw) ScheduleClear

func (AccessorRedraw) ScheduleClear()

Signal the redraw manager to clear both the logical screen and the high resolution canvas before the next Game.Draw().

func (AccessorRedraw) SetManaged

func (AccessorRedraw) SetManaged(managed bool)

Enables or disables manual redraw management. By default, redraw management is disabled and the screen is redrawn every frame.

Must only be called during initialization or Game.Update().

type AccessorScaling

type AccessorScaling struct{}

See Scaling().

func Scaling

func Scaling() AccessorScaling

Provides access to scaling-related functionality in a structured manner. Use through method chaining, e.g.:

mipix.Scaling().SetFilter(mipix.Hermite)

func (AccessorScaling) GetFilter

func (AccessorScaling) GetFilter() ScalingFilter

Returns the current scaling filter. The default is AASamplingSoft.

func (AccessorScaling) GetStretchingAllowed

func (AccessorScaling) GetStretchingAllowed() bool

Returns whether stretching is allowed for screen scaling. See AccessorScaling.SetStretchingAllowed() for more details.

func (AccessorScaling) SetBestFitContextSize

func (AccessorScaling) SetBestFitContextSize(width, height int)

func (AccessorScaling) SetFilter

func (AccessorScaling) SetFilter(filter ScalingFilter)

Changes the scaling filter. The default is AASamplingSoft.

Must only be called during initialization or Game.Update().

The first time you set a filter explicitly, its shader will also be compiled. This means that this function can be effectively used to precompile the relevant shaders. Otherwise, the shader will be compiled the first time it has to be used.

func (AccessorScaling) SetStretchingAllowed

func (AccessorScaling) SetStretchingAllowed(allowed, keepAspectRatio, dynamicScaling bool)

Set to true to avoid black borders and completely fill the screen no matter how ugly it gets. By default, stretching is disabled. In general you only want to expose stretching as a setting for players; don't set it to true on your own.

Must only be called during initialization or Game.Update().

type AccessorTick

type AccessorTick struct{}

See Tick().

func Tick

func Tick() AccessorTick

Provides access to game tick functions in a structured manner. Use through method chaining, e.g.:

currentTick := mipix.Tick().Now()

func (AccessorTick) GetRate

func (AccessorTick) GetRate() int

Returns the current tick rate. Defaults to 1. See AccessorTick.SetRate() for more context.

func (AccessorTick) Now

func (AccessorTick) Now() uint64

Returns the current tick.

func (AccessorTick) SetRate

func (AccessorTick) SetRate(tickRate int)

Sets the tick rate (ticks per update, often refered to as "TPU"). Notice that this is not ebiten.SetTPS(), as ebipixel considers a more advanced model for ticks and updates.

func (AccessorTick) SetUPS

func (AccessorTick) SetUPS(updatesPerSecond int)

This is just ebiten.SetTPS() under the hood, but ebipixel considers a more advanced model for ticks and updates.

func (AccessorTick) TPS

func (AccessorTick) TPS() int

Returns the ticks per second. This is UPS()*TickRate. Notice that this is not ebiten.TPS(), as ebipixel considers a more advanced model for ticks and updates.

func (AccessorTick) UPS

func (AccessorTick) UPS() int

Returns the updates per second. This is ebiten.TPS(), but ebipixel considers a more advanced model for ticks and updates.

type Game

type Game interface {
	// Updates the game logic.
	//
	// You can implement this almost exactly in the same
	// way you would do for a pure Ebitengine game.
	Update() error

	// Draws the game contents.
	//
	// Unlike Ebitengine, the canvas you receive from ebipixel can vary in
	// size depending on the zoom level or camera position, but it always
	// represents pixels one-to-one. Your mindset should be ignoring the
	// canvas size and focusing on "rendering the game area specified by
	// mipix.Camera().Area()".
	Draw(logicalCanvas *ebiten.Image)
}

The game interface for ebipixel, which is the equivalent to ebiten.Game on Ebitengine but without the Layout() method.

type Offscreen

type Offscreen struct {
	// contains filtered or unexported fields
}

Offscreens are logically sized canvases that you can draw to and later project to high resolution space. They simplify the job of drawing pixel-perfect UI and other camera-independent elements of your game (but most of your game should still be drawn directly to the canvas received by Game.Draw(), not offscreens).

Creating an offscreen involves creating an *ebiten.Image, so you want to store and reuse them. They also have to be manually cleared when required.

func NewOffscreen

func NewOffscreen(width, height int) *Offscreen

Creates a new offscreen with the given logical size.

Never invoke this per frame, always reuse offscreens.

func (*Offscreen) Clear

func (self *Offscreen) Clear()

Clears the underlying offscreen canvas.

func (*Offscreen) Coat

func (self *Offscreen) Coat(fillColor color.Color)

Similar to ebiten.Image.Fill(), but with BlendSourceOver instead of BlendCopy.

func (*Offscreen) CoatRect

func (self *Offscreen) CoatRect(bounds image.Rectangle, fillColor color.Color)

Similar to Offscreen.Coat(), but restricted to a specific rectangular area.

func (*Offscreen) Draw

func (self *Offscreen) Draw(source *ebiten.Image, opts *ebiten.DrawImageOptions)

Equivalent to ebiten.Image.DrawImage().

func (*Offscreen) DrawAt

func (self *Offscreen) DrawAt(source *ebiten.Image, transform *ebimath.Transform)

Handy version of Offscreen.Draw() with specific coordinates.

func (*Offscreen) Project

func (self *Offscreen) Project(target *ebiten.Image)

Projects the offscreen into the given target. In most cases, you will want to draw to the active high resolution target of your game (the second argument of a QueueHiResDraw() handler).

func (*Offscreen) Size

func (self *Offscreen) Size() (width, height int)

Returns the size of the offscreen.

func (*Offscreen) Target

func (self *Offscreen) Target() *ebiten.Image

Returns the underlying canvas for the offscreen.

type ScalingFilter

type ScalingFilter uint8

See AccessorScaling.SetFilter().

Multiple filter options are provided mostly as comparison points. In general, sticking to AASamplingSoft is recommended.

const (
	// Anti-aliased pixel art point sampling. Good default, reasonably
	// performant, decent balance between sharpness and stability during
	// zooms and small movements.
	AASamplingSoft ScalingFilter = iota

	// Like AASamplingSoft, but slightly sharper and slightly less stable
	// during zooms and small movements.
	AASamplingSharp

	// No interpolation. Sharpest and fastest filter, but can lead
	// to distorted geometry. Very unstable, zooming and small movements
	// will be really jumpy and ugly.
	Nearest

	// Slightly blurrier than AASamplingSoft and more unstable than
	// AASamplingSharp. Still provides fairly decent results at
	// reasonable performance.
	Hermite

	// The most expensive filter by quite a lot. Slightly less sharp than
	// Hermite, but quite a bit more stable. Might slightly misrepresent
	// some colors throughout high contrast areas.
	Bicubic

	// Offered mostly for comparison purposes. Slightly blurrier than
	// Hermite, but quite a bit more stable.
	Bilinear

	// Offered for comparison purposes only. Non high-resolution aware
	// scaling filter, more similar to what naive scaling will look like.
	SrcHermite

	// Offered for comparison purposes only. Non high-resolution aware
	// scaling filter, more similar to what naive scaling will look like.
	SrcBicubic

	// Offered for comparison purposes only. Non high-resolution aware
	// scaling filter, more similar to what naive scaling will look like.
	// This is what Ebitengine will do by default with the FilterLinear
	// filter.
	SrcBilinear
)

func (ScalingFilter) String

func (self ScalingFilter) String() string

Returns a string representation of the scaling filter.

type TicksDuration

type TicksDuration = internal.TicksDuration

Helper type used for fades and durations of some effects.

const ZeroTicks TicksDuration = 0

Directories

Path Synopsis
This package defines a Shaker interface that the ebipixel camera can use to perform screen shakes, and provides a few default implementations.
This package defines a Shaker interface that the ebipixel camera can use to perform screen shakes, and provides a few default implementations.
This package defines a Tracker interface that the ebipixel camera can use to update its position, and provides a few default implementations.
This package defines a Tracker interface that the ebipixel camera can use to update its position, and provides a few default implementations.
This package defines a Zoomer interface that the ebipixel camera can use to update its position, and provides a few default implementations.
This package defines a Zoomer interface that the ebipixel camera can use to update its position, and provides a few default implementations.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL