ofxLaser openFrameworks Library

repository·main·Indexed 42 days ago

https://github.com/sebleedelisle/ofxlaser

An openFrameworks library for sending vector graphics and point streams to laser controllers (DACs). It provides high-level abstractions for drawing, color correction, zone management, and path optimization, including SVG import, boolean operations on polylines, and coordinate transformation management.

Tokens
10K
Snippets
29
Records
45
Agent score
55%

What's inside ofxLaser

  1. Configure laser render profiles

    main

    ofxLaser uses three render profiles to balance accuracy and speed. You can specify a profile for individual drawing calls using compiler definitions. These profiles can also be edited via the application's GUI and saved as scanner presets.

    • OFXLASER_PROFILE_DEFAULT: The standard profile for most use cases.
    • OFXLASER_PROFILE_FAST: Optimized for smooth lines where high precision is not required.
    • OFXLASER_PROFILE_DETAIL: Optimized for intricate shapes; use with caution as high detail can cause flickering.
  2. How to use ofxLaser in an openFrameworks application

    main

    All API interactions are managed through the ofxLaser::Manager object.

    Lifecycle Workflow

    1. Declaration: Declare an instance of ofxLaser::Manager in your ofApp.h file. It will be automatically initialized.
    2. Update: Call ofxLaser::Manager::update() inside your ofApp::update() method to prepare the system for new graphics.
    3. Drawing: Call your desired drawing methods (e.g., drawLine, drawDot) inside ofApp::draw(). Note that these methods respect standard openFrameworks transformations like ofTranslate, ofRotate, and ofScale.
    4. Sending: Once drawing is complete, call ofxLaser::Manager::send() in ofApp::draw() to transmit the frames to the laser(s).
    5. UI: Use ofxLaser::Manager::drawUI() to render the laser management interface.

    Important: All drawing methods must be called in the sequence: update() $\rightarrow$ draw methods $\rightarrow$ send().

    // In ofApp.h
    ofxLaser::Manager laserManager;
    
    // In ofApp.cpp
    void ofApp::update() {
        laserManager.update();
    }
    
    void ofApp::draw() {
        laserManager.drawLine(ofPoint(0,0), ofPoint(100,100), ofColor::Red);
        laserManager.send();
        laserManager.drawUI();
    }
  3. Install ofxLaser into openFrameworks

    main

    ofxLaser is an addon for openFrameworks 0.12.x. Because it relies on the libera-laser submodule, you must clone the repository with submodules enabled. Downloading the GitHub ZIP is not recommended as it will not include the necessary submodule contents.

    To install, navigate to your openFrameworks addons folder and clone the repository:

    cd openFrameworks/addons/
    git clone --recurse-submodules https://github.com/sebleedelisle/ofxLaser.git

    If you have already cloned the repository without submodules, initialize them manually:

    cd openFrameworks/addons/ofxLaser
    git submodule update --init --recursive
    cd openFrameworks/addons/
    git clone --recurse-submodules https://github.com/sebleedelisle/ofxLaser.git
  4. Manage laser graphics with ofxLaser::Graphic

    main

    The ofxLaser::Graphic class is a container for managing collections of polylines and colors intended for laser drawing. It provides methods to import vector data (SVG), manipulate shapes via boolean operations (subtraction, intersection), and transform the entire graphic.

    Key capabilities include:

    • SVG Import: Load graphics from files, strings, or ofxSVG objects.
    • Shape Manipulation: Subtract polylines from existing shapes, intersect paths, and join touching polylines.
    • Transformations: Translate the graphic, auto-center it, or apply transformations to individual polylines.
    • Serialization: Save and load graphic data using ofJson objects.
    ofxLaser::Graphic graphic;
    // Add shapes
    graphic.addSvg("path/to/file.svg");
    graphic.addPolyline(myPolyline, ofColor(255, 0, 0));
    
    // Manipulate
    graphic.translate(glm::vec3(10, 10, 0));
    
    // Serialize
    ofJson json;
    graphic.serialize(json);
  5. ExPolygons structure for hole management

    main

    The ExPolygons structure (introduced in v4.3.0) is used when you need to explicitly associate 'hole' polygons with their 'outer' container polygons.

    When using the Execute method, you can pass either standard Polygons or the ExPolygons structure as the solution parameter to maintain this parent-child relationship between containers and holes.

  6. How to send messages using LaserMsgEnvelope and LaserAppMsg

    main

    The ofxLaser message bus uses a variant-based pattern to communicate between the Controller and the ManagerBase.

    To send a message, you wrap a specific message struct (from the ofxLaser::LaserMsg namespace) inside a LaserAppMsg variant, and then wrap that variant in a LaserMsgEnvelope. The envelope includes a time field (float) used for latency compensation.

    When receiving messages, you should use std::visit with an overloaded helper to handle the different message types.

    ```cpp
    // Example of creating and sending a message
    using namespace ofxLaser;
    
    LaserMsgEnvelope envelope;
    envelope.time = ofGetElapsedTime();
    envelope.payload = LaserMsg::SetGlobalBrightness{ 0.8f };
    
    // On the receiving end (ManagerBase):
    std::visit(overloaded {
        [&](const LaserMsg::SetGlobalBrightness& msg) { /* handle brightness change */ },
        [&](const LaserMsg::AddLaser& msg) { /* handle adding laser */ },
        // ... handle other types
    }, envelope.payload);
  7. Coordinate range and precision in Clipper

    main

    Clipper uses 64-bit integer coordinates (IntPoint) to ensure numerical robustness and avoid floating-point issues.

    • Coordinate Range: The polygon coordinate range is restricted to +/- 0x3FFFFFFFFFFFFFFF (approximately 4.6e18).
    • Precision: Because it uses 64-bit integers, you can convert floating-point values to and from Clipper's IntPoint structure while retaining a precision of up to 18 decimal places.
    • Historical Note: In older versions (v4.2.4), users could toggle UseFullCoordinateRange to switch between a restricted range (+/- 1,500,000,000) and the full 64-bit range for performance reasons. In current versions, the integer range is managed implicitly.
  8. How LaserState and ManagerBase manage system state

    main

    The ofxLaser system uses a decoupled state management pattern. ManagerBase owns a single LaserState instance and is responsible for keeping it current.

    • ManagerBase: Acts as the source of truth. It updates the state via syncLaserState() immediately before firing any on*() notifications. It also manages views via addLaserView().
    • LaserState: A read-only snapshot of the system state. It contains both live references to complex objects and scalar values for simple properties. Because it contains reference members, it cannot be default-constructed, copied, or moved; it must be initialized once in the ManagerBase member-initializer list.
    • Views: Hold a const LaserState* provided by ManagerBase and can read the state freely without needing to manage ownership or synchronization.
  9. How coordinate transformations work in TransformationManager

    main

    The ofxLaser::TransformationManager class manages a coordinate system for laser mapping using a transformation matrix. It allows you to apply translations, rotations, and scaling to a global coordinate system.

    When you call transformation methods (like translate, rotateDeg, or scale), the currentMatrix is updated. Any points passed through getTransformed() will be mapped from their local coordinates to the transformed coordinate system.

    To prevent transformations from accumulating indefinitely or affecting subsequent drawing operations, use a matrix stack pattern with pushMatrix() and popMatrix(). This allows you to save the current state, apply a set of transformations, and then restore the previous state.

    // Example of using the matrix stack to isolate transformations
    transformationManager.pushMatrix();
        transformationManager.translate(100, 100, 0);
        transformationManager.rotateZDeg(45);
        
        // Transform a point to the new coordinate system
        auto transformedPoint = transformationManager.getTransformed(myPoint);
        // Draw using transformedPoint...
    
    transformationManager.popMatrix();
    // The coordinate system is now back to its previous state
  10. Polygon orientation and filling rules

    main

    Clipper supports multiple polygon filling rules and orientation behaviors:

    • Fill Types: Supports EvenOdd (alternate) and NonZero (winding) rules. Newer versions (v4.6+) also support Positive and Negative polygon fill types.
    • Orientation:
      • The Orientation function is used to determine the direction of polygon vertices.
      • In typical Windows applications (where the Y-axis is positive downwards), outer polygons are oriented clockwise, and inner polygons (holes) have the opposite orientation.
      • The Area function returns a signed value representing the area and orientation.
    • Performance Optimization: If the orientation of output polygons is not important for your application, you can potentially speed up clipping routines (historically by up to 60%) by setting IgnoreOrientation to true (Note: this property was removed in v4.0; check your specific version's API).
  11. Draw shapes and dots to the laser

    main

    Use the following methods via the ofxLaser::Manager instance to render graphics. Most methods accept an optional profile parameter.

    // Lines
    manager.drawLine(start, end, colour, profile);
    manager.drawLine(x1, y1, x2, y2, colour, profile);
    
    // Dots (useful for beam effects)
    // Use intensity (0.0-1.0) to control brightness efficiently
    manager.drawDot(position, colour, intensity, profile);
    manager.drawDot(x, y, colour, intensity, profile);
    
    // Circles
    manager.drawCircle(position, radius, colour, profile);
    manager.drawCircle(x, y, radius, colour, profile);
    
    // Polylines
    manager.drawPoly(ofPolyline, colour, profile);
  12. Render ofxLaser::Graphic objects

    main

    The ofxLaser::Graphic class is used to store multiple polylines, handle shape occlusion, and render SVGs. To output a Graphic object to the laser, use the drawLaserGraphic method.

    Parameters:

    • graphic: The ofxLaser::Graphic instance to draw.
    • brightness: A unit value (float, 0.0 to 1.0) specifying brightness.
    • profile: (Optional) The render profile.
    manager.drawLaserGraphic(myGraphic, 1.0f, OFXLASER_PROFILE_DEFAULT);