g | x | w | all
Bytes Lang Time Link
128Charcoal241207T012017ZNeil
103AWK241206T195648Zxrs
08905AB1E241206T165808ZKevin Cr

Charcoal, 128 bytes

Nθ≔E³Sη≔⌈Eη∧№ι.⌕⮌ι.ζUMη⁺ι×ζ0UMηI⎇№ι.…⁻ι.⁺ζ⌕ι.ι≔Eθ↨⊕ιηε≔⌈Φ⊕⌈↔ε∧ι¬⌈﹪ειδ←⮌E⁺²÷⁻⌈ε⌊εδ⁺|⮌I∕⁻⌈ε×ιδXχζJ⁰¦⁰+Fθ«P↑⁺× ⊕÷⁻§ει⌊εδOP⁺¶I⊕ιL⊕ι→

Try it online! Link is to verbose version of code. Takes input in the order V, M, N, C. Explanation:

Nθ

Input V as a number.

≔E³Sη

Input M, N, C as strings.

≔⌈Eη∧№ι.⌕⮌ι.ζ

Find the maximum number of decimal places in any of the three values.

UMη⁺ι×ζ0

Append that many zeros to each value.

UMηI⎇№ι.…⁻ι.⁺ζ⌕ι.ι

Truncate each value to that many decimals, then remove the decimal point.

≔Eθ↨⊕ιηε

Calculate the y values for x in the range 1 to V.

≔⌈Φ⊕⌈↔ε∧ι¬⌈﹪ειδ

Slowly find the GCD of all of the y values, which will become the y tick step.

←⮌E⁺²÷⁻⌈ε⌊εδ⁺|⮌I∕⁻⌈ε×ιδXχζ

Print all of the y-axis with labels from the highest to the lowest y value, plus one more which will become the origin. (Replace with ﹪﹪%%.0%dfζ to get the values padded with trailing zeros.)

J⁰¦⁰+

Replace the | at the origin with a +.

Fθ«

Loop over the V values.

P↑⁺× ⊕÷⁻§ει⌊εδO

Output the O in the appropriate position.

P⁺¶I⊕ι

Output the x-axis label.

L⊕ι→

Output the x-axis. (Use Lθ→ if you want all of the x-axis to be the same width.)

AWK, 135 104 103 bytes

{for(;i++<$4;p=p" "i){s=sprintf("%5.1f|%"2*i-1"i\n",$1*i^2+$2*i+$3,0)s;t=t"- "}}$0=s"     +"t"\n     "p

Attempt This Online!

AWK, 103 bytes

Limited to V=5

{for(;i++<$4;)s=sprintf("%5.1f|%"2*i-1"s\n",$1*i^2+$2*i+$3,"*")s}$0=s"     +- - - - -\n      1 2 3 4 5"

Attempt This Online!

{for(;i++<$4;)                             # for V
s=sprintf                                  # create pretty string
("%5.1f|%"2*i-1"s\n",$1*i^2+$2*i+$3,"*")s} # * at point on line
$0=s                                       # set output
"     +- - - - -\n      1 2 3 4 5"         # X axis

05AB1E, 89 bytes

Lβ©Zs[ÐïQ#T*¼}￾°/DU/₄z+ÝXz/'|«Déθgj®¾.òR'O¹·ú¹L·._ø'|ýDéθgjR®Xz*ǝR`¨©'+«„- ¹×«¹L®gðך)»

Two loose inputs in the order and format \$V\$ and \$[M,N,C]\$, where \$[M,N,C]\$ are floats.
y-axis of the output will also always be floats.

Try it online or verify all test cases.

Explanation:

Step 1: Same as the previous two challenges:

L                        # Push a list in the range [1, first (implicit) input V]
 β                       # Convert the second (implicit) input-triplet [M,N,C] from a
                         # base-v list to base-10 a integer for each v in the
                         # [1,V]-ranged list
  ©                      # Store this list in variable `®`

Step 2: Calculate the step-size of the y-axis:

Z                        # Push the maximum of the list (without popping)
 s                       # Swap so the list is at the top again
  [                      # Loop indefinitely:
   Ð                     #  Triplicate the current list
    ï                    #  Truncate/floor the current values to integers
     Q                   #  Check if the top two lists are the same
      #                  #  If they are: stop the infinite list
       T*                #  Else: multiply each value by 10
         ¼               #  Increase the counter variable `¾` by 1 (0 by default)
  }                      # After the infinite loop
   ï¿                    # (Cast to integers) and take the GCD (Greatest Common Divisor)
     ¾°                  # Push 10 to the power the counter variable `¾`
       /                 # Divide the GCD by this 10**¾
        DU               # Store a copy in variable `X`

Try just the first two steps online.

Step 3a: Create the generic y-axis rows:

/                        # Divide the values in the list by this
 ₄z+                     #†Add 1/1000 to each
    Ý                    # Pop and push a list in [0,int(^)]
     Xz/                 #†Multiply each by value `X`
        '|«             '# Append "|" to each
           Déθg          # Push its longest value (without popping)
           D             #  Duplicate the list
            é            #  Sort by length (shortest to longest)
             θ           #  Pop and push the last/longest
              g          #  Pop and push its length
               j         # Add leading spaces to make all strings that length

Try it up to this point.

Step 3b: Create the rows containing the Os:

®                        # Push list `®`
 ¾.ò                     #†Round each to `¾` amount of floating points
    R                    # Reverse the list from highest to lowest
     'O                 '# Push "O"
       ¹                 # Push the first input V
        ·                # Double it
         ú               # Add that many leading spaces to the "O"
          ¹L             # Push a list in the range [1,V]
            ·            # Double each value in that ranged list as well
             ._          # Rotate the space-padded "O" that many times left
               ø         # Pair the values in the lists together
                '|ý     '# Join each inner pair by "|"
                   Déθgj # Add leading spaces again if necessary

Try it up to this point.

Step 3c: Insert those O-rows:

R                        # Reverse the list
 ®                       # Push list `®` again
  Xz*                    #†Divide each by `X` to convert them to 1-based indices
     ǝ                   # Insert them into the big list at those 0-based indices
      R                  # Reverse this entire list

Try just the first three steps online.

Step 4: Add the bottom two rows, join by newlines, and output the result:

`                        # Pop and push all rows separately to the stack
 ¨                       # Remove the last character of the last string (the "|" of
                         # row 0.0)
  ©                      # Store this space-padded "0.0" in variable `®` (without
                         # popping)
   '+«                  '# Append a "+" instead
      „-                 # Push string "- "
         ¹×              # Repeat it the first input V amount of times
           «             # Append that to the space-padded "0.0+"
¹L                       # Push a list in the range [1,input V]
  ®g                     # Push the length of the space-padded "0.0" from variable `®`
    ð×                   # Pop and push a string of that many spaces
      š                  # Prepend it to the [1,V]-ranged list
)                        # Wrap all items on the stack into a list again
 »                       # Join the [1,V]-ranged list with prepended string by spaces,
                         # and then join each line by newlines
                         # (after which the result is output implicitly)

† These are all to account for floating-point inaccuracies..

  1. ₄z+: Add \$\frac{1}{1000}\$ (aka 0.001) before truncating it to an integer for the [0,value]-ranged list Ý.
  2. Xz/ (divide by \$\frac{1}{X}\$) instead of X* (multiply by \$X\$) and Xz* (multiply by \$\frac{1}{X}\$) instead of X/ (divide by \$X\$) seem to help to prevent floating-point inaccuracies at those steps.
  3. ¾.ò: Even though all values should already be at that decimal point, the additional round is to fix any floating point inaccuracies from the very first step.

Try it online. without these counter-measures for floating-point inaccuracies to see what goes wrong.