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

draw_arc #5

Closed
Mercerenies opened this issue Jun 14, 2023 · 13 comments
Closed

draw_arc #5

Mercerenies opened this issue Jun 14, 2023 · 13 comments
Labels
accepted 🚀 For accepted issues and pull requests feature ✨ For feature requests and implementations

Comments

@Mercerenies
Copy link

draw_arc behaves like draw_circle but only draws part of the outline of the circle, from a given starting angle to ending angle.

function draw_arc(xx, yy, radius, start_angle, end_angle) {
  draw_primitive_begin(pr_trianglefan);
  draw_vertex(xx, yy);

  for (var i = 0; i < 50; i++) {
    var theta = lerp(start_angle, end_angle, i / 50);
    draw_vertex(xx + radius * dcos(theta), yy - radius * dsin(theta));
  }

  draw_primitive_end();
}

Possible point of expansion: Supporting a "filled" version as well, which draws either a pie slice or a segment rather than just the outline.

@Alphish
Copy link
Owner

Alphish commented Jun 14, 2023

Note sure about this one, as there might be varying arc-drawing needs? Like, someone would want their arc to have smaller or greater precisions, yet another person would want to draw their arc with varying thickness, yet another would use shaders to draw their arcs...
It feels like one of those functions that under its innocent-sounding name hides tons of design decisions that some potential package users would find too disagreeable.

@Alphish Alphish added the feature ✨ For feature requests and implementations label Jun 15, 2023
@MagnusMagnusson
Copy link

Precision and width could be function parameters, or members of the more advanced "draw_arc_ext". Doesn't help people who want to draw things with shaders, but it's a step towards a solution.

@Alphish Alphish added the question ❓ Further information is requested label Aug 24, 2023
@Alphish
Copy link
Owner

Alphish commented Aug 24, 2023

I'm going to leave this one up for discussion, some more research is needed about how people handle their arc-drawing needs. Not saying "no", but with different valid ways to handle it - not just at the implementation level, but at the expected behaviour as well - it might be wise to explore how different people approach arc-drawing before reserving draw_arc name.

Note: It might become less of a concern once Prefabs arrive and the user will be able to override Prefab functions with same named project-specific counterpart. If that will be the case, I won't object to adding some well-liked draw_arc implementation then. ^^

@gnysek
Copy link

gnysek commented Aug 24, 2023

My proposal would be, that for precision we could use default GM value. However I just found out, that there's no way to read it back, so I've made a feature request in advance: YoYoGames/GameMaker-Bugs#3002

@gnysek
Copy link

gnysek commented Aug 24, 2023

btw. on GMLscripts.com there are functions for:
draw_arc, draw_chord, draw_curve and draw_pie - pie and curve looks like they also could be added, especially that it seems they were part of GM5 (however I don't remember that).

@Alphish
Copy link
Owner

Alphish commented Aug 30, 2023

Good point about covering different functions - we might actually want to look into which of these functions we want covered.
I'll return to this feature request after the GMC Jam; FrostyCat suggested adding precision as the last parameter on GMC, that could later be replaced with the circle precision getter, and this might be the way to go. But this would be for the 23.8 release, the 23.4 release is closed now.

@gnysek
Copy link

gnysek commented Aug 30, 2023

Yes, setting precision as last param is a good idea, as it's easy to either remove it, or replace with foo(value = gm_function_name()) as GM allows such things from what I see, so it could be still overridden on demand and backward compatible, when getting current precision would became available.

@Alphish
Copy link
Owner

Alphish commented Aug 19, 2024

I think the version of draw_arc from GMLscripts.com is worth looking into. If I understand correctly, it applies the precision to the ellipse as a whole and then cuts an arc out of it, as opposed to adding the number of segments equal to the precision, which means effectively more detail for low angles and less detail for high angles? This kind of behaviour is worth replicating, so that refardless of the angles combination same sub-arcs will be drawn consistently.

Also, I think draw_pie and draw_chord might as well be added alongside draw_arc, since they need to calculate basically the same curve (maybe they could in fact share some static function for populating vertices along the arc). draw_curve seems to be in a different function category altogether, so I'd rather not include it in this FR.

I'd still like to keep the draw_arc function signature as presented by Mercerenies. Going by center, radius and starting/ending angle seem like a whole lot more convenient way to handle it than defining ellipse bounding box than starting/ending points. So I'm thinking of signatures like:

  • draw_arc(x, y, radius, anglefrom, angleto, precision = 24) (24 is the default precision, so I'd use this until we get draw_get_precision() sort of function)
  • draw_pie(x, y, radius, anglefrom, angleto, outline = false, precision = 24)
  • draw_chord(x, y, radius, anglefrom, angleto, outline = false, precision = 24)

I'd like the implementation to match the precision of the whole circle (e.g. given precision of 24, it would draw 4 segments' worth for 60 degrees angle difference, as opposed to 24 regardless of the angle), like what I think is done in the GMLscripts.com draw_arc and similar functions implementations. It's more technically difficult, but it should make sub-arc segments appearance consistent regardless of how broad or how narrow the angle will end up.

Is everyone on board of that? If that's alright, I might try and draft some implementation, or someone else can provide one.

Once we have the signatures and the precision handling settled, I think it'll be ready for name voting and eventual addition to the toolbox.

@Alphish Alphish pinned this issue Aug 19, 2024
@gnysek
Copy link

gnysek commented Aug 19, 2024

I'm not sure if we could use GM draw_get_circle_precision() when it would became available, as seems that this function under the hood is used for kinda interesting caching of sin and cos values for each of "steps" in circle ( https://github.com/YoYoGames/GameMaker-HTML5//develop/scripts/yyWebGL.js#L316 ).
Drawing for toolbox functions would be done between anglefrom, angleto arguments, so it need to be decided what precision will be - number of segments in full 360 circle (so it would cover ideally over GM circles), number of segments between those angles, or something between (like calculating how many degrees each segment have, so in edge case like 1 degree, we won't draw 24 lines) - complexity of calculations in all those cases are drastically different.

In proposed case (so covering GM native circle), it still need to be decided if it will be rounded, ceiled of floored in case of anglefrom, angleto which aren't equal to 360/precision angles.

In HTML5, default precision seems to be... 36 - https://github.com/YoYoGames/GameMaker-HTML5//develop/scripts/yyWebGL.js#L316 .

@Alphish
Copy link
Owner

Alphish commented Aug 19, 2024

I imagine the arc would be a cut-out of a base circle.

So e.g. if the precision is 24 - with each segment spanning 15 degrees worth - then given angles from 5deg to 128deg it would draw:

  • part of 0-15 segment matching the 5-15 angle range
  • segments 15-30, 30-45, 45-60, 60-75, 75-90, 90-105, 105-120
  • part of 120-135 segment matching the 120-128 angle range

Here is a little graphic showing the general idea, with the green angle range "clipping" to an arc what would otherwise be the 8-precise circle:

image

The most annoying part would be calculating segments intersections for the initial and ending segments, the rest should be pretty straightforward I think.

--- EDIT ---
I figured out somewhat painless method to get the position of the segment start for non-matching angle. Might post an implementation soon...

@gnysek
Copy link

gnysek commented Aug 19, 2024

A picture is worth a thousand words - it perfectly describes what approach will be used, and in that case for sure it could benefit from draw_get_circle_precision().

@Alphish
Copy link
Owner

Alphish commented Aug 19, 2024

Here is the implementation I came up with; it uses only 3 static functions for additional processing. 😛

/// @func draw_arc(x,y,radius,anglefrom,angleto,[precision])
/// @desc Draws a circular line around the given center, with the given radius and between given angles.
/// @arg {Real} x               The x coordinate of the arc center.
/// @arg {Real} y               The y coordinate of the arc center.
/// @arg {Real} radius          The radius of the arc around the center.
/// @arg {Real} anglefrom       The starting angle of the arc.
/// @arg {Real} angleto         The ending angle of the arc.
/// @arg {Real} [precision]     The precision of the circle the arc is a part of; 24 by default.
function draw_arc(_x, _y, _radius, _anglefrom, _angleto, _precision = 24) {
    // static functions    
    static get_segment_position = function(_angle, _segment_angle) {
        _angle = _angle - floor(_angle / _segment_angle) * _segment_angle;
        if (_angle == 0)
            return 0;
        
        var _yfrom = dsin(_angle);
        var _yto = dsin(_angle - _segment_angle);
        return _yfrom / (_yfrom - _yto);
    }
    
    static draw_circle_vertex = function(_x, _y, _radius, _angle) {
        draw_vertex(_x + lengthdir_x(_radius, _angle), _y + lengthdir_y(_radius, _angle));
    }
    
    static draw_midsegment_vertex = function(_x, _y, _radius, _angle1, _angle2, _amount) {
        var _x1 = _x + lengthdir_x(_radius, _angle1);
        var _y1 = _y + lengthdir_y(_radius, _angle1);
        var _x2 = _x + lengthdir_x(_radius, _angle2);
        var _y2 = _y + lengthdir_y(_radius, _angle2);
        draw_vertex(lerp(_x1, _x2, _amount), lerp(_y1, _y2, _amount));
    }
    
    // normalise the angles for easier processing
    var _anglediff = _angleto - _anglefrom;
    if (_anglediff == 0)
        return;
    
    if (_anglediff < 0) {
        _anglediff = -_anglediff;
        _anglefrom = _angleto;
        _angleto = _anglefrom + _anglediff;
    }
    
    // draw a full circle if the arc covers more than 360 degrees
    if (_anglediff >= 360) {
        draw_primitive_begin(pr_linestrip);
        for (var i = 0; i < _precision; i++) {
            draw_circle_vertex(_x, _y, _radius, i * 360 / _precision);
        }
        draw_circle_vertex(_x, _y, _radius, 0);
        draw_primitive_end();
        return;
    }
    
    // draw the actual arc
    var _segment_angle = 360 / _precision;
    var _start_amount = get_segment_position(_anglefrom, _segment_angle);
    var _end_amount = get_segment_position(_angleto, _segment_angle);
    var _start_index = floor(_anglefrom / _segment_angle);
    var _end_index = floor(_angleto / _segment_angle);
    
    draw_primitive_begin(pr_linestrip);
    
    if (_start_amount == 0) {
        draw_circle_vertex(_x, _y, _radius, _anglefrom);
    } else {
        draw_midsegment_vertex(_x, _y, _radius, _start_index * 360 / _precision, (_start_index + 1) * 360 / _precision, _start_amount);
    }
    
    for (var i = _start_index + 1; i <= _end_index; i++) {
        draw_circle_vertex(_x, _y, _radius, i * 360 / _precision);
    }
    
    if (_end_amount > 0) {
        draw_midsegment_vertex(_x, _y, _radius, _end_index * 360 / _precision, (_end_index + 1) * 360 / _precision, _end_amount);
    }
    
    draw_primitive_end();
}

@Alphish Alphish added accepted 🚀 For accepted issues and pull requests and removed question ❓ Further information is requested labels Nov 17, 2024
@Alphish Alphish added this to the 24.6.0 Release milestone Nov 17, 2024
@Alphish
Copy link
Owner

Alphish commented Nov 23, 2024

Implemented and merged!

After some discussions here and there I settled on the naming of:

  • draw_arc
  • draw_circle_sector
  • draw_circle_segment
  • draw_ring (for drawing an area between two radii)
  • draw_ring_arc (for drawing a slice of that area
    Hopefully it'll be clear what each function does, and if not, that it will at least be easy to learn (e.g. circle sector and segments can be looked up on Wikipedia).

@Alphish Alphish closed this as completed Nov 23, 2024
@Alphish Alphish unpinned this issue Nov 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted 🚀 For accepted issues and pull requests feature ✨ For feature requests and implementations
Projects
None yet
Development

No branches or pull requests

4 participants