QB64 Phoenix Edition
Cartesian axes automatically scaled and finding Y on a graph, given the X. - Printable Version

+- QB64 Phoenix Edition (https://staging.qb64phoenix.com)
+-- Forum: QB64 Rising (https://staging.qb64phoenix.com/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://staging.qb64phoenix.com/forumdisplay.php?fid=3)
+---- Forum: Utilities (https://staging.qb64phoenix.com/forumdisplay.php?fid=8)
+---- Thread: Cartesian axes automatically scaled and finding Y on a graph, given the X. (/showthread.php?tid=796)



Cartesian axes automatically scaled and finding Y on a graph, given the X. - bartok - 08-22-2022

[Image: Immagine.png]

Code: (Select All)
'this utility:
'1. given X! and Y! values (the maximum values in abscissa and ordinate of a graph that generally is an output of a
'   part of the code that makes calculations), it automatically scales the Cartesian plane so that (0,0) is at 40
'   pixels in abscissa and ordinate directions from the bottom left corner and the axes end 40 pixels beyond X! and Y!,
'   maintaining 40 pixels from the border;
'2. it draws on the graph the point (X1!,Y!) (where X1! is the X value corresponding to Y! on the graph), it prints
'   its values and it draws 2 dashed lines up to the axes;
'3. it asks an X value on which calculate the corresponding Y value;
'4. it draws on the graph the point (X,Y), it prints its values and it draws 2 dashed lines up to the axes.

OPTION BASE 1

CONST R& = _RGB32(255, 0, 0)
CONST G& = _RGB32(0, 255, 0)
CONST B& = _RGB32(0, 0, 255)
CONST white& = _RGB32(255, 255, 255)
CONST yellow& = _RGB32(255, 255, 0)
CONST grey& = _RGB32(127, 127, 127)
CONST azure& = _RGB32(0, 255, 255)

DIM SHARED DESKTOPWIDTH%, DESKTOPHEIGHT%

DIM schermo&, grafico&
DIM X! 'maximum value in abscissa of a graph. generally they are both an output of a part of the code that makes calculations.
DIM Y! 'maximum value in ordinate of a graph. generally they are both an output of a part of the code that makes calculations.
DIM X1! 'X value corresponding to Y! on the graph. generally they are both an output of a part of the code that makes calculations.
DIM L%, H%

DESKTOPWIDTH% = _DESKTOPWIDTH \ 2
DESKTOPHEIGHT% = _DESKTOPHEIGHT \ 2
L% = DESKTOPWIDTH%: H% = L% \ 1.62 'L e H are defined to create a golden rectangle.
schermo& = _NEWIMAGE(DESKTOPWIDTH%, DESKTOPHEIGHT%, 32)
grafico& = _NEWIMAGE(L%, H%, 32)

SCREEN schermo&
'START OF THE EXAMPLE++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
VIEW PRINT 1 TO 2
X! = 44.66777 '----------------------------------------------------------------> it is generally an output of a part of the code.
Y! = 100.434667 '--------------------------------------------------------------> it is generally an output of a part of the code.
X1! = 3 '----------------------------------------------------------------------> it is the X value corresponding to Y! on the graph and it is generally an output of a part of the code.
CALL DisegnaAssi("X", "Y", X!, Y!, grafico&, R&, white&, white&, yellow&) '----> point 1 of the description.
CALL DisegnaValore(X1!, Y!, azure&, azure&, white&) '--------------------------> point 2 of the description.
LINE (dx!, dy!)-(dx! + X1!, dy! + Y!), yellow& '-------------------------------> it can be any kind of graph generated by a part of the code.
LINE -(dx! + X!, dy!), yellow& '-----------------------------------------------> it can be any kind of graph generated by a part of the code.
_PUTIMAGE (0, (DESKTOPHEIGHT% - _HEIGHT(grafico&) - 16)), grafico&, schermo&
CALL DisegnaPortata(X!, grafico&, schermo&) '----------------------------------> points 3 and 4 of the description.
VIEW PRINT
'END OF THE EXAMPLE++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SLEEP
_FREEIMAGE grafico&
SYSTEM
'--------------------------------------------------------------------------------------------------------------------------------------------------
'--------------------------------------------------------------------------------------------------------------------------------------------------
SUB ScalaOrigine (scala~%, origine~%, X!, Y!, immagine&)
    'scala~%: if 1, it activates the routine "scala", if 0, it does not activate it;
    'origine~%: if 1, it activates the routine "origine", if 0, it does not activate it;
    'X!: maximum value on the abscissa. it could be the last value of a vector or the last value of a vector that is greater than a given theshold;
    'Y!: maximum value in ordinate.
    'So, generally they aren't both directly given as in this example.
    'immagine&: is the image on which the point (0,0) is set by the routine "origine".
    'this subroutine scales the image via WINDOW in an iterative way, so that the point "1" of the description could be reached.
    'generally this SUB is CALLed by the SUB "DisegnaAssi", as we can see in the main code. In that case, both routines "scala" and "origine"
    'are executed, as to say whenever a new graph is made.
    'However, in some cases, it could be necessary to make further graphs on an image already existing without changing the scale. In this
    'case "ScalaOrigine" 'is CALLed alone with parameters (0,1,0,0,immagine&).
    'In other cases, in which we already have a graph properly scaled, it could be necessary to add a further graph not in scale compared to
    'the first. In that case "ScalaOrigine" is CALLed alone with parameters (1,1,Kx*X!,Ky*Y!, immagine&) where Kx and Ky are appropriate
    'coefficient in order to have the wanted result.

    SHARED dx%, dy%
    SHARED dx!, dy!

    DIM fattoreX!, fattoreY!

    fattoreX! = 1
    fattoreY! = 1
    IF scala~% = 0 AND origine~% = 1 THEN GOSUB origine
    IF scala~% = 1 AND origine~% = 1 THEN
        DO
            GOSUB scala
            GOSUB origine
            IF Y! * fattoreY! >= dy! + Y! + dy! + dy! THEN
                IF X! * fattoreX! >= dx! + X! + dx! + dx! THEN
                    EXIT DO
                ELSE
                    fattoreX! = fattoreX! + 0.01
                END IF
            ELSE
                fattoreY! = fattoreY! + 0.01
            END IF
        LOOP
    END IF
    EXIT SUB
    '--------------------------------------------------------------------------------------------------------------------------------------------------
    scala:

    WINDOW 'it closes other WINDOWs already opened with this routine.
    WINDOW (0, 0)-(X! * fattoreX!, Y! * fattoreY!)
    RETURN
    '--------------------------------------------------------------------------------------------------------------------------------------------------
    origine:

    dx% = 39
    dx! = PMAP(dx%, 2)
    dy% = _HEIGHT(immagine&) - 1 - dx%
    dy! = PMAP(dy%, 3)
    RETURN
END SUB
'--------------------------------------------------------------------------------------------------------------------------------------------------
'--------------------------------------------------------------------------------------------------------------------------------------------------
SUB DisegnaAssi (X$, Y$, X!, Y!, immagine&, ColoreCartiglio&, ColoreAssi&, ColoreTacca&, ColoreNumeri&)

    SHARED dx%, dy%
    SHARED dx!, dy!

    DIM x%, y% 'for marks positioning on the axes.
    DIM taccaX!, taccaY! 'they define the range with which to draw the marks.

    _DEST immagine&: CLS
    SELECT CASE X!
        CASE IS >= 12
            taccaX! = X! \ 6
        CASE IS <= 1
            taccaX! = 0.25
        CASE ELSE
            taccaX! = 0.5
    END SELECT
    SELECT CASE Y!
        CASE IS >= 12
            taccaY! = Y! \ 6
        CASE IS <= 1
            taccaY! = 0.25
        CASE ELSE
            taccaY! = 0.5
    END SELECT
    WINDOW
    LINE (0, 0)-(_WIDTH(immagine&) - 1, _HEIGHT(immagine&) - 1), ColoreCartiglio&, B
    CALL ScalaOrigine(1, 1, X!, Y!, immagine&)
    LINE (dx!, dy! + Y! + dy!)-(dx!, dy!), ColoreAssi&: LINE -(dx! + X! + dx!, dy!), ColoreAssi& 'it draws the axis.
    PSET (dx!, dy! + Y! + dy!), ColoreAssi&: DRAW "F20": PSET (dx!, dy! + Y! + dy!), ColoreAssi&: DRAW "G20" 'it draws the arrow of the ordinate axis.
    PSET (dx! + X! + dx!, dy!), ColoreAssi&: DRAW "G20": PSET (dx! + X! + dx!, dy!), ColoreAssi&: DRAW "H20" 'it draws the arrow of the abscissa axis.
    COLOR ColoreNumeri&
    _PRINTSTRING (PMAP(dx! + X! + dx!, 0), dy%), X$
    _PRINTSTRING (dx%, PMAP(dy! + Y! + dy!, 1) - dx% \ 2), Y$
    _PRINTSTRING (dx% \ 4, dy% - 7), "0"
    i% = 1
    WHILE i% * taccaX! <= X! 'it draws and prints the marks and the corresponding values on the abscissa axis.
        LINE (dx! + i% * taccaX!, PMAP(dy% + 5, 3))-(dx! + i% * taccaX!, PMAP(dy% - 5, 3)), ColoreTacca& 'it draws the marks in abscissa direction of 11 pixels lenght.
        x% = PMAP(dx! + i% * taccaX!, 0)
        _PRINTSTRING (x% - 3 * LEN(_TRIM$(STR$(i% * taccaX!))), _HEIGHT(immagine&) - 1 - dx% + 5), _TRIM$(STR$(i% * taccaX!)) 'it prints the value corresponding
        'to the mark, considering that the single character is 8 bits large.
        i% = i% + 1
    WEND
    i% = 1
    WHILE i% * taccaY! <= Y! 'it draws and prints the marks and the corresponding values on the ordinate axis.
        LINE (PMAP(dx% - 5, 2), dy! + i% * taccaY!)-(PMAP(dx% + 5, 2), dy! + i% * taccaY!), ColoreTacca& 'it draws the marks in ordinate direction of 11 pixels lenght.
        y% = PMAP(dy! + i% * taccaY!, 1)
        _PRINTSTRING (dx% \ 4, y% - 7), _TRIM$(STR$(i% * taccaY!)) 'it spaces the value of the mark of 10 pixels from the border and it centrates it on the mark, considering that the chararacter in 16 pixels hight.
        i% = i% + 1
    WEND

END SUB
'--------------------------------------------------------------------------------------------------------------------------------------------------
'--------------------------------------------------------------------------------------------------------------------------------------------------
SUB DisegnaValore (X!, Y!, ColoreLinea&, ColorePunto&, ColoreTesto&)
    'subroutine which draws on the graph the point (X!,Y!), it prints its values and it draws 2 dashed lines up to the axes.
    'In this case:
    'X!: it is X value corresponding to Y!
    'Y!: it is the maximum.
    'Even in this case X! and Y! generally are not given, but they are the result of a part of the code that search them.

    SHARED dx%, dy%
    SHARED dx!, dy!

    DIM x%, y% 'for the positioning on the graph of the point (X!,Y!)

    CIRCLE (dx! + X!, dy! + Y!), PMAP(3, 2), ColorePunto&
    PAINT (dx! + X! + PMAP(0.5, 2), dy! + Y! + PMAP(0.5, 2)), ColorePunto&
    LINE (dx!, dy! + Y!)-(dx! + X!, dy! + Y!), ColoreLinea&, , 65520 '=1111111111110000 where each character is a pixel. 1: the pixel is drawn, 0: the pixel is empty. so we have a dashed line with 12 pixels drawn
    'every 4 empty pixels.
    LINE -(dx! + X!, dy!), ColoreLinea&, , 65520
    x% = PMAP(dx! + X!, 0)
    y% = PMAP(dy! + Y!, 1)
    COLOR ColoreTesto&
    _PRINTSTRING (x% - 4, y% - 16), "(" + _TRIM$(STR$(Arrotonda!(X!))) + ";" + _TRIM$(STR$(Arrotonda!(Y!))) + ")"
END SUB
'--------------------------------------------------------------------------------------------------------------------------------------------------
'--------------------------------------------------------------------------------------------------------------------------------------------------
SUB DisegnaPortata (X!, immagine&, destinazione&)
    'subroutine that uses the subroutine "DisegnaValore" in order to draw and print on the graph the point (X,Y) and its value. the dashed lines are animated.
    'in order to find the Y value, it asks the X value. Then, starting from the pixel corresponding to Y=0 (y%), it proceeds pixel by pixel decreasing the
    'value of y%, until a yellow pixel of the graph is found. While the DO-LOOP cycle goes on searching the yellow pixel, a dashed line is drawn and displayed.
    'similarly a dashed line is drawn towards the ordinate axis, while searching a white pixel.

    SHARED dx%, dy%
    SHARED dx!, dy!

    DIM ore!
    DIM x%, y% 'for the drawn of the dashed lines in real time.

    _DEST schermo&
    DO
        CLS 2
        INPUT "- Type an X value: ", ore!
    LOOP WHILE ore! > X! 'it doesn't axcept values greater than the maximum abscissa value of the graph.
    IF ore! = 0 THEN EXIT SUB
    _DEST immagine&
    _SOURCE immagine&
    CALL ScalaOrigine(0, 1, 0, 0, immagine&)
    x% = PMAP(dx! + ore!, 0)
    y% = PMAP(dy!, 1)
    DO
        y% = y% - 1
        IF POINT(dx! + ore!, PMAP(y%, 3)) = azure& THEN 'in this case the user has chosen an X value corresponding to X!, which is already displayed.
            _DEST schermo&
            EXIT SUB
        END IF
        IF POINT(dx! + ore!, PMAP(y%, 3)) = yellow& THEN EXIT DO 'the yellow pixel corresponds to the value of searched value.
        LINE (dx! + ore!, dy!)-(dx! + ore!, PMAP(y%, 3)), grey&, , 65520 'it draws the dashed line from the abscissa to the yello pixel. The animation is provided by the following _PUTIMAGE.
        _PUTIMAGE (0, (DESKTOPHEIGHT% - _HEIGHT(immagine&) - 16)), immagine&, destinazione&
    LOOP
    portata! = PMAP(y%, 3) - dy!
    CALL DisegnaValore(ore!, portata!, 0, 0, white&) 'via subroutine "DisegnaValore", the valune of (X,Y) is displayed.
    i% = 2
    DO
        x% = x% - 1
        IF POINT(PMAP(x%, 2), PMAP(y%, 3)) = white& THEN EXIT DO 'the condition in order to exit the LOOP is the identification of a white pixel which belongs to the ordinate axis.
        LINE (dx! + ore!, PMAP(y%, 3))-(PMAP(x%, 2), PMAP(y%, 3)), grey&, , 65520 'an animated dashed line is drawn towards the ordinate axis, as before.
        _PUTIMAGE (0, (DESKTOPHEIGHT% - _HEIGHT(immagine&) - 16)), immagine&, destinazione&
    LOOP
    _DEST schermo&
END SUB
'--------------------------------------------------------------------------------------------------------------------------------------------------
'--------------------------------------------------------------------------------------------------------------------------------------------------
FUNCTION Arrotonda! (num!)

    Arrotonda! = _ROUND(num! * 100) / 100
END FUNCTION