Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 Design discussion #58

Open
FaBjE opened this issue Jul 6, 2024 · 2 comments
Open

V2 Design discussion #58

FaBjE opened this issue Jul 6, 2024 · 2 comments

Comments

@FaBjE
Copy link
Contributor

FaBjE commented Jul 6, 2024

Split-off from the discussion in the PR I've put my view for a maintainable codebase that can support many different printers and label types into a plant uml class diagram.

Image: Class Diagram

Edit link Don´t forget to share the link if you made changes. It won´t be saved on the server.

Global program flow:

  • GUI or CLI starts, from now "program"
  • Program asks DeviceManager to detect connected printer (or simulator) and give printer object using factory method.
  • Program selects the desired label type (LabelConfig)
  • Program asks printer object for a "RenderContext" (for the selected label type)
  • GUI: User can design label with preview, user clicks print:
  • Program retrieves the desired stuff that needs to be rendered (from GUI controls or CLI options) and makes makes specific RenderConfig types for the items desired.
  • Program initiates a "RenderManager" with the RenderContext to mange the rendering process.
  • Program calls renderer_factory for every RenderConfig type it has collected previously and adds it as a renderer.
  • Program starts the rendering process (All renderers are called to do their thing on the RenderContext (Bitmap), and retrieves the result).
  • Program prints label by passing RenderContext to printer object that prints a label.

Notes:

  • I have added with/height/x/y coordinates, so that fixed size labels of newer printer types can be supported
  • I did the naming from memory of the current software, keep it the same if it makes no difference
@maresb
Copy link
Contributor

maresb commented Jul 6, 2024

This is super helpful, thanks @FaBjE!!!

Apologies in advance if I'm incorrectly reading this class diagram, I tend to avoid object-oriented programming except for very simple cases, so these diagrams are unfamiliar to me, but they look great!

Could you help me to understand the interface between the rendering pipeline and the printer, which currently consists of just a link between RenderContext and Printer? Is the RenderContext class supposed to be a container for a bitmap being sent by the renderer to the printer? Or is the printer providing information like px_per_mm via the RenderContext to the Renderer?

My mental model of a printer is that it's something capable of printing certain bitmaps. If the RenderContext is indeed a container for bitmaps, could we get by with directly using plain bitmaps? A bitmap already knows its width and height, so that seems redundant. And the printer already knows its px_per_mm.

For the other direction (printer→renderer), it seems to me like the printer (with its label type) needs to tell the renderer, "these are the types of bitmaps I'm capable of printing". My expectation here is very similar to the LabelConfig class. In particular, I'd expect fields max_width_px and max_height_px. (Given the int type, I suspect your intended suffix for width/height is px instead of mm.)

In terms of units, it seems to me like mm is a purely user-facing unit. Of course the print head must have some mechanism to communicate its px_per_mm to the UI, but apart from that, would it be feasible to banish mm to the user-interface and do all the core logic in terms of px?

On a related note, there are many places in the code that use float type for px, which I find really confusing, since we can only print whole pixels. I'd rather have px be always int type, or if that's impossible, let's document very clearly why floats are required.

@FaBjE
Copy link
Contributor Author

FaBjE commented Jul 7, 2024

This is super helpful, thanks @FaBjE!!!

Apologies in advance if I'm incorrectly reading this class diagram, I tend to avoid object-oriented programming except for very simple cases, so these diagrams are unfamiliar to me, but they look great!

Could you help me to understand the interface between the rendering pipeline and the printer, which currently consists of just a link between RenderContext and Printer? Is the RenderContext class supposed to be a container for a bitmap being sent by the renderer to the printer? Or is the printer providing information like px_per_mm via the RenderContext to the Renderer?

My mental model of a printer is that it's something capable of printing certain bitmaps. If the RenderContext is indeed a container for bitmaps, could we get by with directly using plain bitmaps? A bitmap already knows its width and height, so that seems redundant. And the printer already knows its px_per_mm.

RenderContext is indeed a bitmap, with some extra data. Like the px_per_mm. I like to wrap these things in a container object so that they can be passed around easily. I probably missed something that we will need in the future. With this approach it is just a matter of adding it to the RenderContext object and it is available in all the places.
px_per_mm is indeed filled by the printer. The whole RenderContext instance is made by the printer as only it knows the "printable area" for a particular label.

I tried to keep the rendering stuff as separated from the printer as possible. RenderContext are the things that do need to be "passed around".
px_per_mm It's usage is/should be limited, but for example the GUI preview showing margins/sizes in mm will need it.

For the other direction (printer→renderer), it seems to me like the printer (with its label type) needs to tell the renderer, "these are the types of bitmaps I'm capable of printing". My expectation here is very similar to the LabelConfig class. In particular, I'd expect fields max_width_px and max_height_px. (Given the int type, I suspect your intended suffix for width/height is px instead of mm.)

It's a bit of the other way around. You select/indicate a label type to the printer. and you ask the printer for a RenderContext.
Which is basically: This is a "bitmap + metadata" I can print for you on the label, render what you like on it, and I'll print it.

LabelConfig sizes are intended to be in mm. Float may be a better datatype indeed.
Why mm?
Example: We want to support this type of labels: https://www.dymo.com/labels-tapes/labelwriter-labels/dymo-labelwriter-return-address-labels/SAP_30330.html
These are 19x51mm.
If you have a 300 dpi printer, the amount of printable pixels on this label differs from when you have a 600 dpi printer.
To only have 1 LabelConfig type for each type of label, and not a LabelConfig type per label for each printer. We need to use mm.

The printer object will calculate the available pixels with the label sizes and its DPI, subtract it's print margins, and give this as a "Rendercontext".

For the margins we now use a simple lookup table as the amount of supported labels (tapes in this case) is limited.
But I think this is not sustainable with large amounts of labels (just take a look online of the amount of available sizes).
I also think that the printers have some kind of "generic" way to determine margins for a (series) of labels.
This can be added into the printer object in combination with the PrinterConfig to store model specific data.

Technically we can allow the user to set/add custom label sizes (in mm) from for example the GUI as well. In case they have some custom label type.

In terms of units, it seems to me like mm is a purely user-facing unit. Of course the print head must have some mechanism to communicate its px_per_mm to the UI, but apart from that, would it be feasible to banish mm to the user-interface and do all the core logic in terms of px?

All core logic is in px. Indeed the GUI touching parts can use mm. I think the part we differ in view is the LabelConfig. For the reason stated above.

On a related note, there are many places in the code that use float type for px, which I find really confusing, since we can only print whole pixels. I'd rather have px be always int type, or if that's impossible, let's document very clearly why floats are required.

I completely agree with you. (px == int). I see no reason why it can´t be like that.

I think/assume/guess the reason that where pixels are calculated from say a size in mm it can make sense to use a float type as some programming languages (I have no clue how this relates to python!!) can optimize the following:
"int = float / int" to "int = int(float) / int", instead of the expectation of "int = int(float / float(int))".
In text: instead of doing the calculation as a floating point, the input is converted to an int an than calculated as an integer operation.
Reasoning of the compiler: It is becoming an int, so why bother with the precision during the calculation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants